subnormal IEEE 754 floating point numbers support

2020-03-01 08:23发布

While porting an application from Linux x86 to iOS ARM (iPhone 4), I've discovered a difference in behavior on floating point arithmetics and small values.

64bits floating point numbers (double) smaller than [+/-]2.2250738585072014E-308 are called denormal/denormalized/subnormal numbers in the IEEE 754-1985/IEEE 754-2008 standards.

On iPhone 4, such small numbers are treated as zero (0), while on x86, subnormal numbers can be used for computation.

I wasn't able to find any explanation regarding conformance to IEEE-754 standards on Apple's documentation Mac OS X Manual Page For float(3).

But thanks to some answers on Stack Overflow ( flush-to-zero behavior in floating-point arithmetic , Double vs float on the iPhone ), I have found some clues.

According to some searches, it seems the VFP (or NEON) math coprocessor used along the ARM core is using Flush-To-Zero (FTZ) mode (e.g. subnormal values are converted to 0 at the output) and Denormals-Are-Zero (DAZ) mode (e.g. subnormal values are converted to 0 when used as input parameters) to provide fast hardware handled IEEE 754 computation.

  • Full IEEE754 compliance with ARM support code
  • Run-Fast mode for near IEEE754 compliance (hardware only)

A good explanation on FTZ and DAZ can be found in x87 and SSE Floating Point Assists in IA-32: Flush-To-Zero (FTZ) and Denormals-Are-Zero (DAZ):

FTZ and DAZ modes both handle the cases when invalid floating-point data occurs or is processed with underflow or denormal conditions. [...]. The difference between a number that is handled by FTZ and DAZ is very subtle. FTZ handles underflow conditions while DAZ handles denormals. An underflow condition occurs when a computation results in a denormal. In this case, FTZ mode sets the output to zero. DAZ fixes the cases when denormals are used as input, either as constants or by reading invalid memory into registers. DAZ mode sets the inputs of the calculation to zero before computation. FTZ can then be said to handle [output] while DAZ handles [input].

The only things about FTZ on Apple's developer site seems to be in iOS ABI Function Call Guide :

VFP status register | FPSCR | Special | Condition code bits (28-31) and saturation bits (0-4) are not preserved by function calls. Exception control (8-12), rounding mode (22-23), and flush-to-zero (24) bits should be modified only by specific routines that affect the application state (including framework API functions). Short vector length (16-18) and stride (20-21) bits must be zero on function entry and exit. All other bits must not be modified.

According to ARM1176JZF-S Technical Reference Manual, 18.5 Modes of operation (first iPhone processor), the VFP can be configured to fully support IEEE 754 (sub normal arithmetic), but in this case it will require some software support (trapping into kernel to compute in software).

Note: I have also read Debian's ARM Hard Float Port and VFP comparison pages.

My questions are :

  • Where can one find definitive answers regarding subnormal numbers handling across iOS devices ?

  • Can one set the iOS system to provide support for subnormal number without asking the compiler to produce only full software floating point code ?

Thanks.

2条回答
萌系小妹纸
2楼-- · 2020-03-01 09:00
•Where can one find definitive answers regarding subnormal numbers

handling across iOS devices ?

You already found your answer. Its to be expected the ARM doesn't have the same floating-point abilities.

•Can one set the iOS system to provide support for subnormal number without asking the compiler to produce only full software floating point code ?

I do not believe so, at least I would hope so, to get different numbers using the same value.

查看更多
倾城 Initia
3楼-- · 2020-03-01 09:23

Can one set the iOS system to provide support for subnormal number without asking the compiler to produce only full software floating point code?

Yes. This can be achieved by setting the FZ bit in the FPSCR to zero:

static inline void DisableFZ( )
{
    __asm__ volatile("vmrs r0, fpscr\n"
                     "bic r0, $(1 << 24)\n"
                     "vmsr fpscr, r0" : : : "r0");
}

Note that this can cause significant slowdowns in application performance when appreciable quantities of denormal values are encountered. You can (and should) restore the default floating-point state before making calls into any code that does not make an ABI guarantee to work properly in non-default modes:

static inline void RestoreFZ( ) {
    __asm__ volatile("vmrs r0, fpscr\n"
                     "orr r0, $(1 << 24)\n"
                     "vmsr fpscr, r0" : : : "r0");
}

Please file a bug report to request that better documentation be provided for the modes of FP operation in iOS.

查看更多
登录 后发表回答