如何使用 Irvine32 WriteFloat 而非 printf 显示舍入为 .001 的浮点数
Posted
技术标签:
【中文标题】如何使用 Irvine32 WriteFloat 而非 printf 显示舍入为 .001 的浮点数【英文标题】:How to display floating-point rounded to .001 with Irvine32 WriteFloat, not printf 【发布时间】:2020-04-03 21:39:15 【问题描述】:我对汇编语言很陌生,所以请耐心等待......
我有一个浮点数,已四舍五入到最接近的 0.001,但仍显示为 +1.6670000E+000。我希望它显示为 1.667。
这是我对数字进行除法和四舍五入的代码:
fild num_a
fidiv num_b
fimul thousand
frndint
fidiv thousand
fst a_div_b
这是我显示浮动的代码:
mov eax, num_a
call WriteDec
mov edx, OFFSET divide
call WriteString
mov eax, num_b
call WriteDec
mov edx, OFFSET equals
call WriteString
fld a_div_b
call WriteFloat
call CrLf
我在网上查了很多关于如何四舍五入的答案,但它们仍然以扩展格式显示。我正在使用 irvine32 库。
【问题讨论】:
如果您无法访问允许指定格式的函数(例如printf
),则您自己将整数和小数部分分开,并根据数量打印它们之间的点你想要的数字。
有道理,谢谢!我知道如何获得整数部分,但我不知道如何获得小数部分,更不用说只打印 3 位数字了。
得到整数部分后,从原始数字中减去,然后乘以 1000 得到 3 位数。
您可以使用x - (int)x
隔离小数部分(使用截断到 0,而不是默认舍入到最近,例如使用 SSE3 fisttp
。否则您可能会得到一个负小数部分和一个整数部分 1 太高了。当然,使用 SSE2 更容易,您可以使用 cvttsd2si
而不是使用旧版 x87)。要获得前 3 位数字,您已经在使用诸如乘以 1000 之类的技巧来生成一些小数部分整数。
你也可以自己做数字转字符串,循环除以10,每次取余数。这是前导零最简单的方法。
【参考方案1】:
对 cmets 中讨论的方法进行更简单的修改:
乘以 1000 并 转换 使用 fistp
将其转换为整数(默认舍入到最接近的值),而不是仅使用 frndint
舍入到整数值 long double
。 p>
该整数的低 3 位小数是您的数字的小数部分。即您现在有小数定点。 div
by 1000 为您提供商(整数部分)和余数(小数部分)。 用.
打印两个部分。
您需要手动进行 int->string 转换 (How do I print an integer in Assembly Level Programming without printf from the c library?) 或在小数部分打印前导零。 (所以2.062
不会变成2.62
)
这比在 FP 中分成整数和小数部分更容易,这需要舍入并截断为零以确保得到非负小数部分。整数除法自然会向零截断,但传统的 x87 FP->int 转换只能使用默认的舍入模式。 (除了 SSE3 fisttp
。)自引入以来,SSE1/2 具有带有截断或当前舍入模式的 XMM FP->int 转换,例如 cvttsd2si
与 cvtsd2si
缺点:对于较小的浮点输入会溢出 32 位整数,因为单个 32 位整数必须保存 x * 1000
。
另一种方法是使用x - (int)x
获得小数部分,并且只将那个小数部分乘以1000.0
。这会导致(int)x
与小数部分分开,其中x*1000
仅作为浮点存在,而不是int32_t
。
有趣的事实:
AVX512DQ 有直接获取小数部分的指令:VREDUCESD xmm1, xmm2, xmm3/m64, imm8(和 ss/ps/pd 版本)。这是SSE4 roundsd / vrndscalesd 在保留整数部分时会丢弃的部分。更有趣的是:可以保留指定数量的小数位。但当然,这些是二进制小数位,而不是小数位。
如今,大多数 x86 CPU 都具有 SSE4.1,但只有 Skylake-X 高端台式机和现代 Xeon 具有 AVX512DQ。 :/ 和 Ice Lake 笔记本电脑。
【讨论】:
以上是关于如何使用 Irvine32 WriteFloat 而非 printf 显示舍入为 .001 的浮点数的主要内容,如果未能解决你的问题,请参考以下文章
将 foo[si] 之类的 16 位地址与 Irvine32 一起使用(在 32 位模式下)?