powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / Java [игнор отключен] [закрыт для гостей] / предсказуем ли результат?
54 сообщений из 54, показаны все 3 страниц
предсказуем ли результат?
    #38537888
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
возможно без компилятора предсказать результат выполнения такого выражения?
Код: java
1.
 System.out.println(0.3==0.1+0.1+0.1);
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537892
забыл ник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Да
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537894
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
забыл ник,

научишь?
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537899
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
вроде как если дробная часть без потерь переводится в 2 СС, то будет тру, а в остальных случаях по разному.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537905
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
redwhite90,

У меня такой результат:
Код: java
1.
2.
System.out.println(0.3F == 0.1F + 0.1F + 0.1F); // true
System.out.println(0.3 == 0.1 + 0.1 + 0.1); // false

redwhite90возможно без компилятора предсказать результат выполнения такого выражения?С типом double не получится. (имхо)
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537908
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Usman,

вроде как по последней значащей цифре мантиссы это определяется( хотя не уверен)

во всяком случае эта версия объясняет разные результаты для float и double.

P.S.

Код: java
1.
2.
 System.out.println(0.5==0.1+0.1+0.1+0.1+0.1);//true
        System.out.println(0.5f==0.1f+0.1f+0.1f+0.1f+0.1f); //true




0.5 переводится в 2 СС без потерь.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537909
rdm
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537914
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
rdm,

да, проблема та, но это не ответ.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537943
забыл ник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
вам уже намекнули что правильный ответ такой - Числа с вещественной точкой сравнивать через == нельзя Точка.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537946
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
redwhite90, дело в том что дробно-десятичная константа не мапиться в double и float как один
к одному. Там будет аппроксимация дробной части к наиболее близкому числу. Поэтому
само по себе присвоение уже имеет дефект точности.

Чтобы избежать дефектов сравнения договорились сравнивать как

Код: java
1.
System.out.println(abs(0.3 - 0.1+0.1+0.1) < epsilon);



Где эпсилон ты сам задаёшь как критерий точности.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537972
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
mayton, забыл_ник

я это знаю.


просто был вопрос в тесте такой...могу предположить, что и на scjp такой может быть.

собственно ответ там данный, что если реально без потерь перевести дробную часть в двоичную систему счисления - будет тру - я не смог опровергнуть.

А если нет, то оказывается тоже иногда будет true. Хочу узнать возможно ли в уме прикинуть когда true, когда false
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537976
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В уме трудно прикинуть чему будет равно 1.0/3.0 в float, double представлении,
поэтому заниматься этим не стоит.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537977
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
redwhite90,

Как я унал недавно на quizful, чтобы узнать, представляется ли десятичная дробь конечной двоичной, нужно перевести ее в обычную дробь, сократить и посмотреть является ли знаменатель степенью двойки. И сама степень скажет сколько дробных разрядов надо.

0.3 = 3/10 - конечным не получается
1/10 - тоже

т.е. ни 0.3 не будет точным, ни 0.1
в итоге изза последнего разряда оно не сойдется и будет false

из примеров ниже,
0.5 = 5/10 = 1/2 -- число запишется точно
Сумма пяти 0.1 будет округлена до 0.5 скорей всего, так что true
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38537990
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
chabapok,

ну а 0.2 - не переводится в 2 сс, но результат true
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38538019
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
redwhite90,

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
BigDecimal delta = BigDecimal.ONE.divide(BigDecimal.TEN);         // 0.1
BigDecimal n = BigDecimal.valueOf(100);
BigDecimal result = delta.multiply(n);                            // 10.0
BigDecimal number = BigDecimal.ZERO;                              // 0.0
for (int i = 0; i < n.intValue(); i++) {
	number = number.add(delta);                               // number += 0.1;
}
System.out.println(result.equals(number));                        // true
System.out.println(result.doubleValue() == number.doubleValue()); // true
System.out.println(result.floatValue() == number.floatValue());   // true
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38538133
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
redwhite90,

0.2 = 2/10 = 1/5
5 не степень двойки, значит конечным числом не представляется

А true тут не при чем. Это уже к сложению относится - надо смотреть чего там с младшим разрядом получится. Хз как это посмотреть.
Но в тестах должно даваться то, что можно в уме предугадать. (не знаю можно ли понять тут, лично я понять не могу)
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38538170
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Usmanredwhite90,

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
BigDecimal delta = BigDecimal.ONE.divide(BigDecimal.TEN);         // 0.1
BigDecimal n = BigDecimal.valueOf(100);
BigDecimal result = delta.multiply(n);                            // 10.0
BigDecimal number = BigDecimal.ZERO;                              // 0.0
for (int i = 0; i < n.intValue(); i++) {
	number = number.add(delta);                               // number += 0.1;
}
System.out.println(result.equals(number));                        // true
System.out.println(result.doubleValue() == number.doubleValue()); // true
System.out.println(result.floatValue() == number.floatValue());   // true



если честно не понял, что я должен тут увидеть.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38538171
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
chabapokredwhite90,

0.2 = 2/10 = 1/5
5 не степень двойки, значит конечным числом не представляется

А true тут не при чем. Это уже к сложению относится - надо смотреть чего там с младшим разрядом получится. Хз как это посмотреть.
Но в тестах должно даваться то, что можно в уме предугадать. (не знаю можно ли понять тут, лично я понять не могу)
источник вопроса

http://www.quizful.net/TestStoreAction.viewQuestion?question=UOcsHuSsfIa2#5558656655373698788
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38538179
Фотография Usman
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
redwhite90если честно не понял, что я должен тут увидеть.Увидеть разницу!
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
double delta = 1.0 / 10;
double n = 100;
double result = delta * n;
double number = 0;
for (int i = 0; i < n; i++) {
	number += delta;
}
System.out.println(result == number); // false

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
float delta = 1.0F / 10F;
float n = 100;
float result = delta * n;
float number = 0;
for (int i = 0; i < n; i++) {
	number += delta;
}
System.out.println(result == number); // false

P.S.
Хотелось донести тот факт, что BigDecimal работает с вещественными числами намного предсказуемее .
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38538369
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
UsmanХотелось донести тот факт, что BigDecimal работает с вещественными числами намного предсказуемее .


тяжело не согласиться
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38539219
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
redwhite90источник вопроса...

вот тут еще можно похожий вопрос посмотерть - www.quizful.net/TestStoreAction.viewQuestion?question=BM85bA43XmMK
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38540517
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
chabapok,

пожалуй даже сюда запостю:

авторpublic class Main {
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
 public static void main(String[] args) { 
        try { 
            String value = "29.1"; 
            System.out.println((Float.valueOf(value) + 1.0) == 30.1); 
            System.out.println((Double.valueOf(value) + 1.0) == 30.1); 
            System.out.println(Float.valueOf(value)/0); 
            System.out.println(Double.valueOf(value)/0); 
        } 
        catch (NumberFormatException ex) { 
            System.out.println("NumberFormatException"); 
        } 
        catch (ArithmeticException ex) { 
            System.out.println("ArithmeticException"); 
        } 
    } 


ответ:

1: Float.valueOf(value) дает 29.1f. Далее 29.1f приводится к double т.к. 1.0 имеет тип double (по-умолчанию все литералы с плавающей точкой имеют тип double). Получаем 29.100000381469727d. Соответственно (Float.valueOf(value) + 1.0)=30.100000381469727d. И в результате (Float.valueOf(value) + 1.0) == 30.1 равно false.
2: Аналогично п.1 только без приведения float к double, все переменные типа double. (Double.valueOf(value) + 1.0) дает 30.1d. И в результате (Double.valueOf(value) + 1.0) == 30.1 равно true.
3: Деление float на ноль не дает ArithmeticException (только деление целого типа на ноль даст ArithmeticException), для таких случаев в классе Float даже определена константа: public static final float POSITIVE_INFINITY = 1.0f / 0.0f;
Причем если заглянуть в исходники метода java.io.PrintStream#println(float), то становится ясно что float превращается в строку с помощью метода String.valueOf(float), который POSITIVE_INFINITY преобразует в "Infinity".
4: Аналогично п.3.
}

Что скажете? разве правда?

Если даже следовать его логике, то 29.1 не переводится в двоичную CC => 29.1 +1 не равно 30.1
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38540678
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
redwhite90,

для меня это такая же тайна, покрытая мраком.
Правил перевода float -> double я не нашел, но судя по личным тестам, "новые" биты заполняются рандомными значениями. Реверс-инженирингом не получается понять чего оно так работает, а документации не нашел.

причем, по всей видимости, тут java вообще не при чем, надо смотреть стандарт IEEE-754

Пока я не могу обьяснить почему оно так работает.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38540688
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Не должно быть там ничего рандомного.

Посмотрите как работает онлайн калькулятор для IEE 754 для 32-х битных вещественных чисел.
http://www.h-schmidt.net/FloatConverter/
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38540956
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
У этого формата нет запрещённых комбинаций но есть маска при которой число
являеся Не-числом (Nan) двух подвидов и бесконечность (Infinity) со знаками "+" и "-"

Тоесть из 4 млрд состояний float числа небольшая часть будут иметь смысл.

И область допустимых значений на всём диапазоне - неравномерна. Тоесть
чем больше число - тем грубее идёт шаг.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38541226
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonНе должно быть там ничего рандомного.

Посмотрите как работает онлайн калькулятор для IEE 754 для 32-х битных вещественных чисел.
http://www.h-schmidt.net/FloatConverter/

Спасибо за ссылку. Посмотрел.
К сожалению, ответа на свой вопрос не нашел. Лучше бы я не смотрел, теперь вообще не ясно.
например число 0.1

мантисса 123 или 2^-4
экспонента - 1.600000023841858 или 5033165

а ниже формула:
The value of a IEEE-754 number is computed as: sign * (2^exponent) * mantissa

И вот смотрю я на эти числа, и на формулу, и понимаю, что не понимаю как из мантиссы и экспоненты, подставив их в формулу получить хоть что-то близкое к 0.1. В 123 степень возводить двойку - явно многовато, а в -4 -- явно маловато.

И что происходит с мантиссой и экспонентой при конвертации float->double тоже неясно. Каким-то образом новые разряды заполняются какими-то значениями, но откуда они берутся - непонятно.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38541356
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
chabapok, у Генри Уоррена в 15 главе описывается IEEE. Книжку реально
найти в pdf-формате бесплатно. Еще на сайте rsdn.ru был исходник конвертера.
Можно посмотреть.

К слову я тоже "пошагово" не знаю алгоритм преобразования числа в float.
Знаю только общие принципы и неоторые хитрости касаемые этого типа
данных.

По поводу конвертации float->double. Начиная с процессоров 386DX все операции
с плавающей точкой делаются в процессоре специальным модулем FPU (Floating
Point Unit), который имеет 8 регистров по 80 бит (extended double), завёрнутых
в кольцевой стек и ВСЕ операции расчётов с FP математикой происходят в нём.
Причём float и double в вычислениях не участвуют а используются просто
как усечённый формат для сериализации exdended double. Тоесть происходит
кастинг, расчёты и еще раз кастинг обратно.

Код: plaintext
1.
double=> extended double => расчёты => обратно в double.



Float вообще нет смысла использовать для хранения в виде переменных.
Он - откровенно слабый. Его возможно пользуют как float[] или float[][]
в некоторых графических и звуковых приложениях где нужно оперировать
в оперативке большими массивами выборок звука в PCM (Pulse Code Modulation),
или матрицами пикселов в LAB (для Photoshop к примеру).
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38541645
Сергей Арсеньев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
mayton
Код: plaintext
1.
double=> extended double => расчёты => обратно в double.



Увы и ах. Эра x87 канула в лету. Засилие C не дало развернуться типу Extended (не портабле видишь ли). И в наступившем эре SSE расчеты стали вестись c одинарной (4 байта), а с достижением прогресса - двойной точностью (8 байт).
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38541677
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ура. И еще раз ура. Давайте только посмотрим какой код генерит JRE. Я приму
эту позицию.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38541749
Сергей Арсеньев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonДавайте только посмотрим какой код генерит JRE.

Как известно, JRE в этом смысле довольно консервативна и даже иногда искуственно повторяет ошибки старых процессоров в вычислениях по соображениям совместимости.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38541765
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сергей Арсеньев, ОК. Я понял. Тема интересная но подняли мы ее не в том форуме.
Я-бы тоже хотел подискутировать на тему % использования SSE.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38542517
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonFloat вообще нет смысла использовать для хранения в виде переменных.

Это не важно. В вопросах оно есть. Кроме того, бывает в базе лучше хранить флоаты, например с целью экономии места.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38542859
redwhite90
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
про процессоры это конечно всё классно и интересно, но по поводу этого поста можете что-нибудь высказать?

http://www.sql.ru/forum/actualutils.aspx?action=gotomsg&tid=1073497&msg=15481944
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38543104
Сергей Арсеньев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
redwhite90,
Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
   System.out.print("29.1f + 1.0f == 30.1f is "); 
   System.out.println( 29.1f + 1.0f == 30.1f); 
   System.out.print("29.1f + 1.0f == 30.1d is "); 
   System.out.println( 29.1f + 1.0f == 30.1d); 
   System.out.print("29.1f + 1.0d == 30.1d is "); 
   System.out.println( 29.1f + 1.0d == 30.1d); 
   System.out.print("29.1d + 1.0f == 30.1d is "); 
   System.out.println( 29.1d + 1.0f == 30.1d); 
   System.out.print("29.1d + 1.0f == 30.1f is "); 
   System.out.println( 29.1d + 1.0f == 30.1f); 
   System.out.print("29.1d + 1.0d == 30.1d is "); 
   System.out.println( 29.1d + 1.0d == 30.1d); 
   System.out.print("30.1f == 30.1d is "); 
   System.out.println( 30.1f == 30.1d); 
   System.out.print("((float)30.1d) == 30.1f is "); 
   System.out.println( ((float)30.1d) == 30.1f);
   System.out.print("((double)30.1f) == 30.1d is "); 
   System.out.println( ((double)30.1f) == 30.1d);


Результат29.1f + 1.0f == 30.1f is true
29.1f + 1.0f == 30.1d is false
29.1f + 1.0d == 30.1d is false
29.1d + 1.0f == 30.1d is true
29.1d + 1.0f == 30.1f is false
29.1d + 1.0d == 30.1d is true
30.1f == 30.1d is false
((float)30.1d) == 30.1f is true
((double)30.1f) == 30.1d is false
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38543952
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сергей Арсеньев, давайте перейдём на С++. Это не будет нарушением
топика. Просто нам в скоупе обсуждения IEEE арифметики удобнее
будет использовать хардкорные кастинги C/C++ и достать бинарное значение
float и double чтобы "подсмотреть" где погрешность.

P.S. Блажкович не закрывай нас :) please.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38543955
Фотография Blazkowicz
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonP.S. Блажкович не закрывай нас :) please.
Вы мне льстите. Я тут не модерирую. Но с интересом слежу за темой.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38543965
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну ... ничего. Кольцо власти найдёт хозяина. А я пошёл искать ЖСС компиллятор под винду.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38544305
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Зачем!??? Си не нужно.

Код: java
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
public class JavaApplication2 {

    public static void main(String[] args) {
        float ff = 0.1f;
        double df = ff;
        double dd= 0.1;

        finePrint("0.1f=   ", ff);
        finePrint("0.1f->d=", df);
        finePrint("0.1d=   ", dd);
        finePrint("1.0 =   ", 1.0);
        finePrint("-1.0=   ", -1.0);
        
        
        System.out.println(ff);
        System.out.println(df);
    }
    
    public static void finePrint(String prepend, double v){
        String r = Long.toBinaryString( Double.doubleToRawLongBits(v) );
        while(r.length()<64){ 
            r = "-"+r;
        }
        System.out.println(prepend+r);
    }
    
    public static void finePrint(String prepend, float v){
        String r = Integer.toBinaryString( Float.floatToRawIntBits(v) );
        while(r.length()<32){
            r = "-"+r;
        }
        r = "   "+r;
        System.out.println(prepend+r);
    } 
}




Результат смотреть в шрифте monospace, там пробелы подгаданы чтобы выглядело красиво
Лучше всего в какой-нибудь блокнот закопипастить

Код: java
1.
2.
3.
4.
5.
6.
7.
0.1f=      --111101110011001100110011001101
0.1f->d=--11111110111001100110011001100110100000000000000000000000000000
0.1d=   --11111110111001100110011001100110011001100110011001100110011010
1.0 =   --11111111110000000000000000000000000000000000000000000000000000
-1.0=   1011111111110000000000000000000000000000000000000000000000000000
0.1
0.10000000149011612




Отсюда видно, что при конвертации float->double новые разряды экспоненты забиваются нулями. Почему мне раньше показалось иначе - не знаю, видимо был в угаре. :)
Отсюда так же видно, что печать работает по-разному для одинаковых чисел - в зависимости от того float оно или double


Дальше мое ИМХО, т.е. могут быть ошибки:

Тут самая первая единичка (которая там где -1) - это знак, дальше 11 бит (или 7 бит) экспонента, и все остальное - мантисса.
старший бит экспоненты равен 0 (выводится как "-").

При этом, экспонента надо, чтобы могла быть как положительная, так и отрицательная. Поэтому, в стандарте принято записывать экспоненту, добавляя 127 или 1023 в зависимости от float оно или double. Поэтому, чтобы получить экспоненту, надо вычесть из представления экспоненты 127 или 1023. поэтому экспонента 1.0 равна 0.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38544308
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Ну... я чесно говоря другое хотел посмотреть. У меня завис вопрос по FPU/SSE.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38544345
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сформулируйте пожалуйста ваш вопрос. А то вроде вкользь FPU/SSE упоминалось, но что именно вам неясно - неясно. :)

насколько я понял, информация ниже может вам показаться интересной.

gcc версия 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char** argv){
    double y = rand()/1.5;//тут rand чтобы компилятор не заоптимайзил
    printf("%g\n", y );
    return 0;
}



Компилим без каких бы то ни было ключиков (gcc main.c) и дизассемблим (objdump -d a.out), получаем такое:
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
  400544:	55                   	push   %rbp
  400545:	48 89 e5             	mov    %rsp,%rbp
  400548:	48 83 ec 20          	sub    $0x20,%rsp
  40054c:	89 7d ec             	mov    %edi,-0x14(%rbp)
  40054f:	48 89 75 e0          	mov    %rsi,-0x20(%rbp)
  400553:	e8 f8 fe ff ff       	callq  400450 <rand@plt>;
  400558:	f2 0f 2a c0          	cvtsi2sd %eax,%xmm0
  40055c:	 f2 0f 10 0d 24 01 00 	movsd  0x124(%rip),%xmm1        # 400688 <_IO_stdin_used+0x10>
  400563:	00 
  400564: 	f2 0f 5e c1          	divsd  %xmm1,%xmm0
  400568:	f2 0f 11 45 f8       	movsd  %xmm0,-0x8(%rbp)
  40056d:	b8 80 06 40 00       	mov    $0x400680,%eax
  400572:	f2 0f 10 45 f8       	movsd  -0x8(%rbp),%xmm0
  400577:	48 89 c7             	mov    %rax,%rdi
  40057a:	b8 01 00 00 00       	mov    $0x1,%eax
  40057f:	e8 ac fe ff ff       	callq  400430 <printf@plt>;
  400584:	b8 00 00 00 00       	mov    $0x0,%eax
  400589:	c9                   	leaveq 
  40058a:	c3                   	retq   



как видно, используется 128-битный xmm, в который влазит два 64-битных дабла.

откомпилим теперь тот же код с ключиком -mno-sse

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
400544:	55                   	push   %rbp
  400545:	48 89 e5             	mov    %rsp,%rbp
  400548:	48 83 ec 30          	sub    $0x30,%rsp
  40054c:	89 7d ec             	mov    %edi,-0x14(%rbp)
  40054f:	48 89 75 e0          	mov    %rsi,-0x20(%rbp)
  400553:	e8 f8 fe ff ff       	callq  400450 <rand@plt>;
  400558:	89 45 e8             	mov    %eax,-0x18(%rbp)
  40055b:	db 45 e8             	fildl  -0x18(%rbp)
  40055e:	dd 05 24 01 00 00    	fldl   0x124(%rip)        # 400688 <_IO_stdin_used+0x10>
  400564:	de f9                	fdivrp %st,%st(1)
  400566:	dd 5d f8             	fstpl  -0x8(%rbp)
  400569:	b8 80 06 40 00       	mov    $0x400680,%eax
  40056e:	48 8b 55 f8          	mov    -0x8(%rbp),%rdx
  400572:	48 89 14 24          	mov    %rdx,(%rsp)
  400576:	48 89 c7             	mov    %rax,%rdi
  400579:	b8 00 00 00 00       	mov    $0x0,%eax
  40057e:	e8 ad fe ff ff       	callq  400430 <printf@plt>;
  400583:	b8 00 00 00 00       	mov    $0x0,%eax
  400588:	c9                   	leaveq 
  400589:	c3                   	retq  



тут видно, что заюзалось fpu
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38544362
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
У меня вот что.
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
#include <stdio.h>
#include <math.h>

int main(int argc,char **argv){

	float fx = 1.0f;
	float fy = 3.0f;
	float fz = fx / fy;

	printf("Float: %f %f %f\n",fx,fy,fz);

	double dx = 1.0;
	double dy = 3.0;
	double dz = dx / dy;

	printf("Double: %g %g %g\n",dx,dy,dz);

	long double lx = 1.0L;
	long double ly = 3.0L;
	long double lz = lx / ly;

	printf("Extended: %Lf %Lf %Lf\n",lx,ly,lz);

	return 0;
}



Гну-шный ассебмлер довольно странный. Не узнаю команд. Ну да ладно.
Ожидал увидеть команду finit/fninit но не нашёл. Странно. Наверное
для современных CPU ее можно скипать? Команды семейства fld* вроде
бы принадлежат к FPU но выглядят странновато.

Хм...

И printf для long double как-то странно форматнул вывод.

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
	.file	"fpu.cpp"
	.def	___main;	.scl	2;	.type	32;	.endef
	.section .rdata,"dr"
LC2:
	.ascii "Float: %f %f %f\12\0"
LC5:
	.ascii "Double: %g %g %g\12\0"
LC8:
	.ascii "Extended: %Lf %Lf %Lf\12\0"
	.text
.globl _main
	.def	_main;	.scl	2;	.type	32;	.endef
_main:
LFB8:
	pushl	%ebp
LCFI0:
	movl	%esp, %ebp
LCFI1:
	andl	$-16, %esp
LCFI2:
	subl	$144, %esp
LCFI3:
	call	___main
	movl	$0x3f800000, %eax
	movl	%eax, 140(%esp)
	movl	$0x40400000, %eax
	movl	%eax, 136(%esp)
	flds	140(%esp)
	fdivs	136(%esp)
	fstps	132(%esp)
	flds	132(%esp)
	flds	136(%esp)
	flds	140(%esp)
	fxch	%st(2)
	fstpl	20(%esp)
	fstpl	12(%esp)
	fstpl	4(%esp)
	movl	$LC2, (%esp)
	call	_printf
	movl	$0, %eax
	movl	$1072693248, %edx
	movl	%eax, 120(%esp)
	movl	%edx, 124(%esp)
	movl	$0, %eax
	movl	$1074266112, %edx
	movl	%eax, 112(%esp)
	movl	%edx, 116(%esp)
	fldl	120(%esp)
	fdivl	112(%esp)
	fstpl	104(%esp)
	movl	104(%esp), %eax
	movl	108(%esp), %edx
	movl	%eax, 20(%esp)
	movl	%edx, 24(%esp)
	movl	112(%esp), %eax
	movl	116(%esp), %edx
	movl	%eax, 12(%esp)
	movl	%edx, 16(%esp)
	movl	120(%esp), %eax
	movl	124(%esp), %edx
	movl	%eax, 4(%esp)
	movl	%edx, 8(%esp)
	movl	$LC5, (%esp)
	call	_printf
	movl	$0, %eax
	movl	$-2147483648, %edx
	movl	$16383, %ecx
	movl	%eax, 80(%esp)
	movl	%edx, 84(%esp)
	movl	%ecx, 88(%esp)
	movl	$0, %eax
	movl	$-1073741824, %edx
	movl	$16384, %ecx
	movl	%eax, 64(%esp)
	movl	%edx, 68(%esp)
	movl	%ecx, 72(%esp)
	fldt	80(%esp)
	fldt	64(%esp)
	fdivrp	%st, %st(1)
	fstpt	48(%esp)
	movl	48(%esp), %eax
	movl	52(%esp), %edx
	movl	56(%esp), %ecx
	movl	%eax, 28(%esp)
	movl	%edx, 32(%esp)
	movl	%ecx, 36(%esp)
	movl	64(%esp), %eax
	movl	68(%esp), %edx
	movl	72(%esp), %ecx
	movl	%eax, 16(%esp)
	movl	%edx, 20(%esp)
	movl	%ecx, 24(%esp)
	movl	80(%esp), %eax
	movl	84(%esp), %edx
	movl	88(%esp), %ecx
	movl	%eax, 4(%esp)
	movl	%edx, 8(%esp)
	movl	%ecx, 12(%esp)
	movl	$LC8, (%esp)
	call	_printf
	movl	$0, %eax
	leave
LCFI4:
	ret
LFE8:
	.def	_printf;	.scl	2;	.type	32;	.endef



Компиллятор realgcc.exe (GCC) 4.5.2 под Windows.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38544370
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Разные компиляторы с разными ключиками генерят разный код. Если у вас gcc-совместимый, можете попробовать ключик -msse2 ?
Ваш код у меня юзает fpu только для long double.

Ну так а ваш вопрос в чем?
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38544371
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
кстати я не очень спец по fpu но разве finit не логичней, чтобы делалось при загрузке ядра?
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38544701
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
chabapok, msse2 не повлиял на ассемблерный вывод.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38545063
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В плане синусов-косинусов тоже странная ситуация. Я ожидал в коде видеть инструкции fsin/fcos/fsincos.
Но гнушный компиллер внезапно вставил каллбеки куда-то. Причём для всех типов.

Код: plaintext
1.
2.
3.
4.
5.
6.
	fldt	48(%esp)
	fstpl	32(%esp)
	fldl	32(%esp)
	fstpl	(%esp)
	call	_sin
	fstpt	48(%esp)


Определение вот

Код: plaintext
1.
2.
FE8:
	.def	_sin;	.scl	2;	.type	32;	.endef



Еще кучерявее. Теперь мне уже интересен
бенчмарк.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38545141
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
А это уже недостаток си. Ну или достоинство - смотря с позиции чего смотреть. У него синус - считается либой, под линукс даже надо линковать ее (ключик -lm). Может ли комплитор заинлайнить эту функцию? При динамической линковке? При статической? Теоретически, вроде может. Но на практике ни gcc ни clang этого не сделали. Синус, кстати, через sse тоже может считаться. По крайней мере, при статической линковке видно некую довольно большую функцию __sin_sse2. Может поэтому и не заинлайнило.

у меня кстати gcc перед вызовом синуса делал какие-то манипуляции с xmm0, что говорит в пользу обсчета функции через sse.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38545336
Сергей Арсеньев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
как знал, что до синусов дело дойдет. :)
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38545434
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Спасибо. Замечательно.

Вольный перевод, насколько я понял: несмотря на то, что на улице 2014, синус все еще считается приближенно, и есть несколько методов приближения, которые дают немножко разный результат. jvm старается посчитать правильней, тогда как FPU это делает в зависимоти от имплементации - или правильней, или быстрее(лажаясь уже в 5 знаке после запятой), причем в большинстве случаев - "быстрее".

Так же есть серьезная проблема посчитать синус, если число сильно выходит за пределы [-пи/4, -пи/4], потому что надо делить на периодическую дробь, а размер периода не точный, значит будет накопление ошибки. При этом, если совсем неповезет, любитель считать на FPU может попасть в космос. К счастью jvm предпринимает меры, чтобы накопления ошибки не было.

Всегда недоблюбливал радианы, и теперь наконец могу обосновать почему.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38545475
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
chabapokсинус все еще считается приближенно, и есть несколько методов приближения, которые дают немножко разный результат. jvm старается посчитать правильней, тогда как FPU это делает в зависимоти от имплементации - или правильней, или быстрее(лажаясь уже в 5 знаке после запятой), причем в большинстве случаев - "быстрее".
Ну... он всегда считался приближённо. А как вы обнаружили расхождение?
Особенно в части FPU/long double.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38545641
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Как я обнаружил расхождение? Элементарно, Ватсон - двумя соощениями выше Сергей Арсеньев дал ссылку, я по ней сходил, прочитал что там написано по английски, и с трудом перевел. Вот так "я обнаружил расхождение"! :))

Впринципе, там очевидные вещи написаны по большей части, но я о них никогда не задумывался.

Ну то есть получается, что вычисление синуса через call куда-то в дебри либы - навскидку правильней, а через fsin - быстрей.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38545645
Сергей Арсеньев
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
chabapok jvm старается посчитать правильней,
Правда их "правильный" sin(Pi) еще чуть дальше от 0 чем "неправильный". :)
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38545784
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Сергей Арсеньев,

я не очень спец в fpu, но насколько я смог, вышло как-то так:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
#include <stdio.h>
#include <stdlib.h>
#define _USE_MATH_DEFINES
#include <math.h>


double sin_fpu (double x){
    double r = 0.0l;
    asm("fldl %1;"
	"fsin;"
        "fstpl %0;" : "=m"(r): "m"(x) );
    return r;
}

int main(int argc, char ** argv){
    double x = sin(M_PI);
    printf("%.19g\n", x);
    x = sin_fpu(M_PI);
    printf("%.19g\n", x);
    return 0;
}



Результат:
1.224646799147353207e-16
1.224606353822377258e-16

Действительно, второе число ближе к нулю. И java считает по первому вариану. Хотя по вашей ссылке написано что java точнее. Странно. Видимо, sse не точное.
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38545917
Фотография mayton
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
В math.h число определено с double precision
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
/* Traditional/XOPEN math constants (double precison) */
#ifndef __STRICT_ANSI__
#define M_E 2.7182818284590452354
#define M_LOG2E 1.4426950408889634074
#define M_LOG10E 0.43429448190325182765
#define M_LN2 0.69314718055994530942
#define M_LN10 2.30258509299404568402
#define M_PI 3.14159265358979323846



Думаю что недостаток младших разрядов (нехватка точности по сравнению с long double)
фактически нам говорит о том что в радианах M_PI это не развёрнутый 180 угол
(против часовой стрелки от оси OX) а слегка уменьшенный угол вида 180-epsilon.

Я не знаю как работает численный метод который расчитывает синус в FPU
(скорее всего это расчёт суммы ряда Тейлора или Лорана) но аргумент
перед подачей нормализуют (приводят к [ -M_PI/2.0 , M_PI/2.0 ])
и уже на этой операции мы можем иметь дефект аргумента. Тоесть
мы получаем не синус нуля а синус epsilon.

IMHO
...
Рейтинг: 0 / 0
предсказуем ли результат?
    #38546353
chabapok
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
maytonаргумент
перед подачей нормализуют (приводят к [ -M_PI/2.0 , M_PI/2.0 ])
и уже на этой операции мы можем иметь дефект аргумента.
IMHO

насколько я понял из той ссылки, FPU не может на этапе приведения вычитать математическое пи, поэтому приводит тоже по своей упрощенной формуле, которая на больших числах приведет нас в космос.

То есть, считая sin(M_PI) у нас M_PI не математическое, но и приведение к диапазону не использует математиически точное пи. В этом случае логично было бы предположить, что FPU должно было вычесть свое PI своей максимальной точности, которое равно M_PI, но тогда получился бы точно ноль.

Причем, M_PI-atan(1)*4 дает 0, это намекает, что M_PI задефайнено с максимальной точностью.
...
Рейтинг: 0 / 0
54 сообщений из 54, показаны все 3 страниц
Форумы / Java [игнор отключен] [закрыт для гостей] / предсказуем ли результат?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]