【发布时间】:2013-07-10 21:04:47
【问题描述】:
我正在尝试计算各种输入的 IEEE-754 32 位浮点平方根,但对于一个特定输入,以下基于 Newton-Raphson 方法的算法不会收敛,我想知道我能做什么解决问题?对于我正在设计的平台,我有一个 32 位浮点加法器/减法器、乘法器和除法器。
对于输入 0x7F7FFFFF (3.4028234663852886E38)。,算法不会收敛到正确答案 18446743523953729536.000000 这个算法的答案是 18446743523953737728.000000。
在硬件实现之前,我使用 MATLAB 来实现我的代码。我只能使用单精度浮点值,(所以没有双精度)。
clc; clear; close all;
% Input
R = typecast(uint32(hex2dec(num2str(dec2hex(((hex2dec('7F7FFFFF'))))))),'single')
% Initial estimate
OneOverRoot2 = single(1/sqrt(2));
Root2 = single(sqrt(2));
% Get low and high bits of input R
hexdata_high = bitand(bitshift(hex2dec(num2hex(single(R))),-16),hex2dec('ffff'));
hexdata_low = bitand(hex2dec(num2hex(single(R))),hex2dec('ffff'));
% Change exponent of input to -1 to get Mantissa
temp = bitand(hexdata_high,hex2dec('807F'));
Expo = bitshift(bitand(hexdata_high,hex2dec('7F80')),-7);
hexdata_high = bitor(temp,hex2dec('3F00'));
b = typecast(uint32(hex2dec(num2str(dec2hex(((bitshift(hexdata_high,16)+ hexdata_low)))))),'single');
% If exponent is odd ...
if (bitand(Expo,1))
% Pretend the mantissa [0.5 ... 1.0) is multiplied by 2 as Expo is odd,
% so it now has the value [1.0 ... 2.0)
% Estimate the sqrt(mantissa) as [1.0 ... sqrt(2))
% IOW: linearly map (0.5 ... 1.0) to (1.0 ... sqrt(2))
Mantissa = (Root2 - 1.0)/(1.0 - 0.5)*(b - 0.5) + 1.0;
else
% The mantissa is in range [0.5 ... 1.0)
% Estimate the sqrt(mantissa) as [1/sqrt(2) ... 1.0)
% IOW: linearly map (0.5 ... 1.0) to (1/sqrt(2) ... 1.0)
Mantissa = (1.0 - OneOverRoot2)/(1.0 - 0.5)*(b - 0.5) + OneOverRoot2;
end
newS = Mantissa*2^(bitshift(Expo-127,-1));
S=newS
% S = (S + R/S)/2 method
for j = 1:6
fprintf('S %u %f %f\n', j, S, (S-sqrt(R)));
S = single((single(S) + single(single(R)/single(S))))/2;
S = single(S);
end
goodaccuracy = (abs((single(S)-single(sqrt(single(R)))))) < 2^-23
difference = (abs((single(S)-single(sqrt(single(R))))))
% Get hexadecimal output
hexdata_high = (bitand(bitshift(hex2dec(num2hex(single(S))),-16),hex2dec('ffff')));
hexdata_low = (bitand(hex2dec(num2hex(single(S))),hex2dec('ffff')));
fprintf('FLOAT: T Input: %e\t\tCorrect: %e\t\tMy answer: %e\n', R, sqrt(R), S);
fprintf('output hex = 0x%04X%04X\n',hexdata_high,hexdata_low);
out = hex2dec(num2hex(single(S)));
【问题讨论】:
-
18446743523953729536 和 18446743523953737728 都不能用单精度表示。最接近的可表示值分别是 18446742974197923840 和 18446744073709551616。所以你永远不会得到数学上正确的结果,除非你打印一个双精度值,否则我看不出你是如何得到 18446743523953737728 结果的。
-
好的,正确的结果是0x5F7FFFFF。该算法收敛到 0x5F800000。你明白吗?
-
请注意,获得最接近的可表示值只会减少错误;它不会消除它,因此它本身并不能保证错误不会在以后的计算中“爆发”。
-
你有融合乘加吗?您能否将 (r/s+s)/2 计算为 s+(r-s*s)/s,其中分子是使用融合乘加计算的?
-
java Math.sqrt() 方法可以正确舍入 - 双精度结果是 0x43efffffeffffffc,向下舍入到浮点数是 0x5f7fffff。 API 文档要求对 sqrt 结果进行正确舍入。有关参考实现,请参阅 (stackoverflow.com/questions/825221/…) 的答案。代码需要修改为 32 位而不是 64 位。
标签: algorithm matlab math floating-point computer-science