如何以可移植的方式编写此浮点代码?
How to write this floating point code in a portable way?
我正在研究一种加密货币,节点必须进行计算:
average /= total;
double ratio = average/DESIRED_BLOCK_TIME_SEC;
int delta = -round(log2(ratio));
要求无论系统使用什么架构或标准库,每个节点都有完全相同的结果。我的理解是 log2 可能有不同的实现,产生的结果略有不同,或者 --ffast-math 之类的标志可能会影响输出结果。
是否有一种简单的方法可以将上述计算转换为可验证地跨不同架构移植的东西(定点?),或者我是否过度考虑了所需的精度(假设我在最后四舍五入了答案)。
编辑:平均值是长整型,总计是整数...所以平均值最终四舍五入到最接近的秒数。
DESIRED_BLOCK_TIME_SEC = 30.0(这是一个浮点数)即#defined
为了使这种计算准确无误,必须准确计算所有除法和对数——或者倒推。
-round(log2(x)) == round(log2(1/x))
,意思是其中一个除法可以翻转得到(1/x) >= 1.
round(log2(x)) == floor(log2(x * sqrt(2))) == binary_log((int)(x*sqrt(2)))
.
这里的一个小细节是,如果 (double)sqrt(2)
向下或向上舍入。如果向上舍入,则可能存在一个或多个值 x * sqrt2 == 2^n + epsilon
(四舍五入后),如果向下舍入,我们将得到 2^n - epsilon
。一个会给出 n
的整数值,另一个会给出 n-1
。哪个是正确的?
当然那个是对的,和理论中点x * sqrt(2)
的比值小
x * sqrt(2) / 2^(n-1) < 2^n / (x * sqrt(2))
-- 乘以 x*sqrt(2)
x^2 * 2 / 2^(n-1) < 2^n
-- 乘以 2^(n-1)
x^2 * 2 < 2^(2*n-1)
为了使这种比较准确,x^2 or pow(x,2)
在边界上也必须准确——这很重要,原始值的范围是多少。在展开 x = a/b
时可以而且应该进行类似的分析,以便以乘法可能溢出为代价来减轻除法的不精确性...
再一次,我想知道所有其他类似的应用程序如何处理极端情况,这些情况甚至可能不存在——假设 average
和 total
很小,这些情况可能会被强力搜索足够的整数。
编辑
因为 average
是一个整数,所以将 -round(log2(average))
.
边界上的那些精确整数值制成表格是有意义的
从八度开始:d=-round(log2((1:1000000)/30.0)); find(d(2:end) ~= find(d(1:end-1))
1 2 3 6 11 22 43 85 170 340 679 1358 2716
5431 10862 21723 43445 86890 173779 347558 695115
All the averages between [1 2( -> 5
All the averages between [2 3( -> 4
All the averages between [3 6( -> 3
..
All the averages between [43445 86890( -> -11
int a = find_lower_bound(average, table); // linear or binary search
return 5 - a;
不需要浮点运算
我正在研究一种加密货币,节点必须进行计算:
average /= total;
double ratio = average/DESIRED_BLOCK_TIME_SEC;
int delta = -round(log2(ratio));
要求无论系统使用什么架构或标准库,每个节点都有完全相同的结果。我的理解是 log2 可能有不同的实现,产生的结果略有不同,或者 --ffast-math 之类的标志可能会影响输出结果。
是否有一种简单的方法可以将上述计算转换为可验证地跨不同架构移植的东西(定点?),或者我是否过度考虑了所需的精度(假设我在最后四舍五入了答案)。
编辑:平均值是长整型,总计是整数...所以平均值最终四舍五入到最接近的秒数。
DESIRED_BLOCK_TIME_SEC = 30.0(这是一个浮点数)即#defined
为了使这种计算准确无误,必须准确计算所有除法和对数——或者倒推。
-round(log2(x)) == round(log2(1/x))
,意思是其中一个除法可以翻转得到(1/x) >= 1.
round(log2(x)) == floor(log2(x * sqrt(2))) == binary_log((int)(x*sqrt(2)))
.
这里的一个小细节是,如果 (double)sqrt(2)
向下或向上舍入。如果向上舍入,则可能存在一个或多个值 x * sqrt2 == 2^n + epsilon
(四舍五入后),如果向下舍入,我们将得到 2^n - epsilon
。一个会给出 n
的整数值,另一个会给出 n-1
。哪个是正确的?
当然那个是对的,和理论中点x * sqrt(2)
的比值小
x * sqrt(2) / 2^(n-1) < 2^n / (x * sqrt(2))
-- 乘以 x*sqrt(2)x^2 * 2 / 2^(n-1) < 2^n
-- 乘以 2^(n-1)x^2 * 2 < 2^(2*n-1)
为了使这种比较准确,x^2 or pow(x,2)
在边界上也必须准确——这很重要,原始值的范围是多少。在展开 x = a/b
时可以而且应该进行类似的分析,以便以乘法可能溢出为代价来减轻除法的不精确性...
再一次,我想知道所有其他类似的应用程序如何处理极端情况,这些情况甚至可能不存在——假设 average
和 total
很小,这些情况可能会被强力搜索足够的整数。
编辑
因为 average
是一个整数,所以将 -round(log2(average))
.
从八度开始:d=-round(log2((1:1000000)/30.0)); find(d(2:end) ~= find(d(1:end-1))
1 2 3 6 11 22 43 85 170 340 679 1358 2716
5431 10862 21723 43445 86890 173779 347558 695115
All the averages between [1 2( -> 5
All the averages between [2 3( -> 4
All the averages between [3 6( -> 3
..
All the averages between [43445 86890( -> -11
int a = find_lower_bound(average, table); // linear or binary search
return 5 - a;
不需要浮点运算