2013-10-28 59 views
0

我有一个为Windows编写的C++代码(Visual Studio),我需要将它移植到Java中。这不是很容易,目前我被困在使用正弦函数。来自Linux(经过测试以进行比较)和Java的结果与Windows源的结果不同。两个结果都是错误的,但这并不重要。结果是完全一样的,这很重要。微软和Java/Linux之间正弦函数的结果不同

我会在底部发布整个源代码。例如,我需要计算5174852443848405000.0的正弦值。我知道这是一个非常大的,也许是不寻常的数字,但我不能改变这一点。 Linux和Java返回0.153662和Windows约0.16xx。 函数“random_value_genrator()”被使用大约500,000次,因此结果的差异可能稍后会发生。

initial_value_generator将计算稍后由random_value_generator函数使用的值。该值由FILETIME对象和三个常量生成。缓冲区溢出正在发生但未处理。 random_value_generator每次使用时都会修改DWORD64 prng_initial_value。

我能够成功构建initial_value_generator函数。

我想我不能完成这个任务,但任何帮助表示赞赏。

有些全局变量:

DWORD64 prng_initial_value = 0; 

DWORD64 CON1_RVG = 0x4F3D859E; 
double CON2_RVG = 0.946270391; 

DWORD64 CON1_PRNG = 0x2682D10B7; 
DWORD64 CON2_PRNG = 0x19254D38000; 
DWORD64 CON3_PRNG = 0x0F1E34A09; 

此功能在程序启动时使用一次。将一个较大的DWORD64写入prng_initial_value,稍后由random_value_generator()使用。 的系统时间是由常数1(缓冲区溢出)相乘,由常数除以2,并用恒定3.

void initial_value_generator() 
{ 
    SYSTEMTIME systime; 
    FILETIME filetime; 

    // Systemzeit zu GMT-Format umwandeln 
    SystemTimeToFileTime(&systime,&filetime); 

    prng_initial_value = (*(DWORD64*)&filetime) * CON1_PRNG/CON2_PRNG + CON3_PRNG; 
} 

此功能在每次使用时改变所述DWORD64 prng_initial_value加入。

int random_value_generator() 
{ 
    double sin_value; 
    double copy_of_prng_initial_value; 
    DWORD64 prng_con1; 
    double result; 

    // Initialen Wert aus dem initial_random_generator in lokaler Variable speichern 
    copy_of_prng_initial_value = prng_initial_value; 

    // Sinus vom initialen Wert 
    sin_value = sin(copy_of_prng_initial_value); 

    // Initialen Wert mulipikation mit einem konstanten Wert (0x4F3D859E) 
    prng_con1 = prng_initial_value * CON1_RVG; 

一些进一步的计算变得疯狂:

result = prng_con1 + sin_value; 
    result = result * copy_of_prng_initial_value; 
    result = result + CON2_RVG; 
    result = result * copy_of_prng_initial_value; 

    // Das Ergebnis aus der Logarithmus Rechnung addieren 
    result += log(copy_of_prng_initial_value); 

    // Das Ergebnis aus den Berechnungen als Pointer in die 
    // Speicheradresse von prng_initial_value als double Pointer speichern. 
    *(double*)&prng_initial_value = result; 

    // Rueckgabe des Berechneten Wert als Integer 
    return prng_initial_value; 
} 

仅供参考我发表我的Java代码(所有评论都是英文)。随机函数看起来有点疯狂,因为我正在测试很多东西。我对此非常抱歉。但重要的一点是Math.sin(double x)函数的使用,其结果与使用Microsoft C++编译器的Math.h中的sin函数不同。

private final long initialValue; 
private long randomValue; 
final BigInteger uint64MaxValue = new BigInteger("18446744073709551616"); //2^64 

public ConfickerC() { 
    this.initialValue = this.generateInitialValue(); 
    this.randomValue = this.initialValue; 
} 

private long generateInitialValue() { 
    //We need the actual date without the time from GMT +0 timezone 
    Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT")); 
    cal.set(Calendar.HOUR_OF_DAY, 0); 
    cal.set(Calendar.MINUTE, 0); 
    cal.set(Calendar.SECOND, 0); 
    cal.set(Calendar.MILLISECOND, 0); 

    long systemtimeAsFiletime = cal.getTimeInMillis(); 

    /* 
    * Goal is to get the above created date into Windows FileTime format. 
    * The Windows FileTime format has got 100 nano seconds per tick. 
    * So one increment of the long value results further 100 nano seconds. 
    * Instead of Unix the FileTime format begins with 1st January 1601 - not 1970. 
    * 11644473600 is the interval between 1601 and 1970 in seconds. 
    * Java has got a resolution of 1 ms per tick unix have got 1 second per 
    * tick. So first devide the time by 1000. Then add the interval. 
    * After this we multiply by 10 million to get a resolution of 100 
    * nano seconds per tick. 
    */ 
    systemtimeAsFiletime /= 1000;  //divide by 1000 to get seconds instead of milliseconds 
    systemtimeAsFiletime += 11644473600L; //add x seconds to add the interval between 1601 and 1970 
    systemtimeAsFiletime *= 10000000L; //Windows FileTime has a resolution of 100 nano seconds per tick; so multiply by 10M 

    /* 
    * The virus is calulating for getting the initial value: time * con1/con2 + con3 
    * Originaly there occurs a buffer overflow which is not handled in the C++ code. 
    * The funny thing is that Java does not have a DWORD64 (unsinged _int64). So because of this bit missing (and so the overflow is different) we need BigInteger. 
    * Because BigInteger has no 2^64 limitation we need the maximul value of DWORD64. 
    * This is used to "simulate" the buffer overflow by calculating ((time * con1) % 2^64)/con2 + con3 
    * modulo 2^64 will result a value which is equal to the C++ calculation 
    */ 

    final BigInteger CONSTANT_1 = new BigInteger("10337718455");    //Original: 0x2682D10B7 
    final BigInteger CONSTANT_2 = new BigInteger("1728000000000");    //Original: 0x19254D38000 
    final BigInteger CONSTANT_3 = new BigInteger("4058204681");     //Original: 0x0F1E34A09 

    BigInteger bigIntSystemTime = BigInteger.valueOf(systemtimeAsFiletime); 

    //Return as long value: ((time * con1) % 2^64)/con2 + con3 
    return bigIntSystemTime.multiply(CONSTANT_1).divideAndRemainder(uint64MaxValue)[1].divide(CONSTANT_2).add(CONSTANT_3).longValue(); 
} 

private int generateRandomValue() { 
    final long CONSTANT_1 = 1329431966L; 
    final double CONSTANT_2 = 0.946270391; 
    double result = 0.0; 
    double copyOfInit = this.randomValue; 

    System.out.println(System.getProperty("line.separator") + "Copy of init: " + copyOfInit); 
    System.out.printf("Copy of init: %f\n", copyOfInit); 
    double sinInit = Math.sin(copyOfInit); System.out.println("Sinus: " + sinInit); 
    System.out.printf("Sinus: %f\n", sinInit); 
    System.out.println("Sinus gerundet: " + Math.round(sinInit*1000000)/1000000.0d); 
    BigInteger b =  BigInteger.valueOf(this.randomValue).multiply(BigInteger.valueOf(CONSTANT_1)).divideAndRemainder(uint64MaxValue)[1]; 

    System.out.println("Init * Konstante 1: " + b); 
    BigDecimal bd = new BigDecimal(b.toString()); 
    //bd.add(BigDecimal.valueOf(sinInit)); 

    //result = t + sinInit; System.out.println("Multi + Sinus: " + result); 
    result = bd.add(BigDecimal.valueOf(sinInit)).doubleValue(); System.out.println("Multi + Sinus: " + result); 
    result *= (long) this.randomValue; System.out.println("Zwischenergebnis * init: " + result); 
    result += CONSTANT_2; System.out.println("Konstante 2 addiert: " + result); 
    System.out.printf("BigD: %s", BigDecimal.valueOf(result).multiply(BigDecimal.valueOf(randomValue))); 
    result *= this.randomValue; System.out.printf("Erneut mit init multipliziert: %f", result); 
    double l = Math.log((long)this.randomValue); System.out.println("Log von init: " + l); 
    result += l; System.out.printf("+= mit Log: %f\n", result); 

    this.randomValue = (long)result; System.out.printf("Ende: %d\n", this.randomValue); 

    this.randomValue = Double.doubleToRawLongBits(result); 

    return (int)this.randomValue; 
} 
+2

这看起来很有趣。你能简化你的代码到复制错误所需的最小数量,以便我们可以排除代码中存在另一个错误的可能性吗? – templatetypedef

+0

@templatetypedef说了什么,SSCCE在这里会很有用。您也可以发布您的Java源代码,以排除在端口过程中不存在错误的可能性。虽然我确信sin(x)的结果可能会因语言不同而不同(由于计算方法稍有不同),但它不应该那么大。 – Radiodef

+2

采取这么大数目的正弦没有意义。正弦值仅在[0,2 * PI]范围内有趣。对于所有的值,“sin(x)= sin(x%(2 * PI))”。当你使用这么大的数字时,你已经失去了影响正弦计算值范围内的所有重要性。你需要重新考虑你的算法。 – pburka

回答

2

三角函数是具有合理模糊规范的库函数。例如,这里是C标准必须在这个话题(7.12.4.6)说:

罪功能

简介

#include <math.h> 
double sin(double x); 
float sinf(float x); 
long double sinl(long double x); 

说明

sin函数计算x的正弦(以弧度测量)。

返回

sin函数返回罪x

因此,他们会使用不同的算法和不同的精度,即,使用该库的版本,你不会得到完全相同的结果。例如,不同的库可能会在精度和计算速度之间做出不同的折衷。即使库的实现完全相同,您可能也不会在不同的系统上得到完全相同的结果,因为在整个计算过程中可能会在不同点处对值进行舍入。为了在不同平台之间获得相当接近的结果,您可能需要在这些平台上实现相同的算法。

请注意,sin(x)明确提供[0, π/2]范围内的最佳结果。将大量数字传递给sin(x)可能会产生相当差的近似值,但我认为大多数实现将在做任何计算之前将x映射到上面给出的范围内。理想情况下,您应该从一开始就避免使用大数值,并用π的倍数表示。但是,即使x属于上述范围,您可能会得到不同实施方式的不同结果。

+1

是否有可能获得微软在Visual Studio中使用的正弦函数的实现来尝试至少移植它?如果在正弦函数中传递的巨大数字会导致错误的近似值,那么这种方式可能是作者的一个不错选择,使得将该函数转换为其他语言更加困难。如果我必须使用Linux,使用Wine模拟器执行此代码是否有意义?你可以这样做吗? –

+0

您可以从[Dinkumware](http://dinkumware.com)获得微软的实施:据我所知,Dinkumware为微软提供了标准的C和C++库(顺便说一句,看着我得到的相当老的版本,实现的第一件事似乎是将较大的值缩放到首选范围内)。或者,您可能希望持续使用自己的实现,例如基于[数字食谱](http://en.wikipedia.org/wiki/Numerical_Recipes)。 –

+1

如果我正确理解评论线程,目标不是计算“sin(x)”,而是将所有输入的Dinkumware的'sin(x)'实现再现到最后一位。 – MSalters

相关问题