如何使用 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】:

对 cme​​ts 中讨论的方法进行更简单的修改:

乘以 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 转换,例如 cvttsd2sicvtsd2si

缺点:对于较小的浮点输入会溢出 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 的浮点数的主要内容,如果未能解决你的问题,请参考以下文章

使用 IRVINE32.inc 在 MASM 中检查回文

将 foo[si] 之类的 16 位地址与 Irvine32 一起使用(在 32 位模式下)?

如何将Assembly(irvine masm)中的字符串初始化回null?

如何使用 DWORD PTR(汇编语言)打印负整数

assembler 几个小程序

ReadConsoleInputA 引发访问冲突