У меня есть код C ++, написанный для Windows (Visual Studio), который мне нужно перенести на Java. Это не очень легко, и в настоящее время я застрял с использованием функции синуса в. Результаты, полученные от Linux (проверено на предмет сравнения) и Java, отличаются от результатов, полученных из источника Windows. Оба результата неверны, но это не имеет значения. Важно, чтобы результаты были точно такими же.
Я опубликую весь источник внизу. Например, мне нужно рассчитать синус 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), результат которой отличается от функции sin в Math.h с использованием компилятора Microsoft C ++.
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;
}
Тригонометрические функции — это библиотечные функции с довольно неопределенной спецификацией. Например, вот что говорит стандарт 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
попадает в диапазон выше.
Других решений пока нет …