Что нового

Autoit разучился считать?

babanty

Новичок
Сообщения
34
Репутация
1
Ниже указан код, если его включить на тест и нажимать enter, то где-то к 5.76 число внезапно становится 5.7600000000001 и далее ошибка накапливается (число становится 5,25000...02).
Подскажите с чем это связано?

Код:
Global $t = 2
While 1
	$t -= 0.01
	if($t < 5) Then $t += 1
	MsgBox(0, "Test", $t)
	Sleep(10)
WEnd
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
http://kuzelenkov.narod.ru/mati/book/inform/inform5.html
 

AZJIO

Меценат
Меценат
Сообщения
2,879
Репутация
1,194
babanty
с чем это связано?
Раздели 1 на 3, у тебя будет 3 в периоде. Есть дробные числа, которые нельзя представить в виде законченного числа, от этого на краю числа появляется округление. У Autoit по заявлению разработчиков есть функция борющаяся с этой проблемой, но видимо это грань где нельзя отбросить погрешность, ведь она может оказаться правильной.
Либо округляй до удобной тебе величины, либо если тебе нужна великая точность, то эта погрешность достаточно мала, для точности числа
Кстати, тема поднималась ни единожды.
 
Автор
B

babanty

Новичок
Сообщения
34
Репутация
1
AZJIO сказал(а):
babanty
с чем это связано?
Раздели 1 на 3, у тебя будет 3 в периоде. Есть дробные числа, которые нельзя представить в виде законченного числа, от этого на краю числа появляется округление. У Autoit по заявлению разработчиков есть функция борющаяся с этой проблемой, но видимо это грань где нельзя отбросить погрешность, ведь она может оказаться правильной.
Либо округляй до удобной тебе величины, либо если тебе нужна великая точность, то эта погрешность достаточно мала, для точности числа
Кстати, тема поднималась ни единожды.

Единственный выход оказался округлять. Так ведь тут ситуация не такая как при 1/3 = 0,333..., тут вычитается из числа 6 одна сотая 24 раза, и на моменте "5.77-0.01" у него получается 5.76000000000001, то есть запустите этот код ниже и будет та же самая ошибка.
Код:
$t = 6
For $i = 0 To 30
	$t -= 0.01
	MsgBox(0, "Test", $t)
Next


Если это связано с тем как записываются числа, то почему же это не работает в этих примерах:
Код:
MsgBox(0, "Test", 5.77-0.01)
MsgBox(0, "Test", 6-(0.01*24))


Или в этом:
Код:
$t = 2 ; отличие от первого кода, там было 6
For $i = 0 To 30
	$t -= 0.01
	MsgBox(0, "Test", $t)
Next
 

AZJIO

Меценат
Меценат
Сообщения
2,879
Репутация
1,194
babanty
В десятичной системе тебе кажется очевидным числа 0,01, в двоичной системе очевидное выглядит как 0,(3) (тройка в периоде) и поэтому операция с таким числом несёт в себе неточность на краю, так как отсекается к примеру 9-ка на конце и если округлить было бы из 19 в 20, но отсекая становится 10.У тебя число на конце диапазона фальшивит. Ты вычисляешь в цикле 30 раз, естественно эта фальшь начинает влиять на крайний регистр числа. Ещё раз: то что тебе кажется очевидным в десятичной системе, не очевидно в двоичной системе. А лучший вариант, просто переведи число в двоичную систему и складывай их, каждый раз переводя результат в десятичную систему. И поймёшь в какой момент появляется погрешность.
Мне просто лень это делать, может в интернете есть статья, поищи. Но это не от языка зависит, а от формы представления числа и разрядности. Какую бы то не выбрал разрядность у тебя просто после пятидесяти нолей появится единичка, или после тысячи нолей. Твоя задача после вычисления откинуть две последние числа в 00 и вычислять с той же разрядностью.
Например 7 в двоичной системе 111, это как для десятичиной 999, или число 1024 выглядит как 10000000000, соответственно 1000 будет выглядеть не таким округлённым (1111101000) в десятичной системе.
 

C2H5OH

AutoIT Гуру
Сообщения
1,473
Репутация
333
Для представления чисел с плавающей запятой используются 64 бита, из них под мантиссу отводится 52.
Для выполнения операции с числом порядка 2+2, отнимаемая Вами 0,01, имеющая ненормализованное представление
1010001111010111000010100011110101110000101000111101 с порядком 2-6,
будет сдвинута до порядка 2+2 и её мантисса будет иметь вид
0000000001010001111010111000010100011110101110000101
что фактически является числом 0,00999999999999979
и отличается от 0,01 на 0,00000000000000021.
Поэтому, отнимая в цикле 0,01, Вы накапливаете погрешность и в итоге получаете ощутимую разницу,
а однократно отняв 5.77-0.01 или 6-(0.01*24)), вы не отловите накопление разницы.
 
A

Alofa

Гость
Код:
$t = 0
For $i = 1 To 20000
	$t -= 0.625
	If $t < 5 Then $t += 12
Next
MsgBox(0, "Test", $t)

:D
 
Верх