Search code examples
c++armrapidjson

How to isolate a fault on arm device?


I am using rapidjson on an Arm device and get strange behaviour, when running this code.

    #include <document.h>
    using namespace std;

int main()
{
    const char json []="[{\"Type\":\"float\",\"val_param\" : 12.025 }]";

    rapidjson::Document d;

    if( d.Parse<0>( json ).HasParseError() ) {
     //ErrorCase
    }else{
        rapidjson:: Value& val_param    = d[0]["val_param"];
        double tmp_double1  = val_param.GetDouble(); 
        cout << tmp_double1 <<endl; // -9.2559641157289301e+61 instead of 12.025
    }

    return 0;
}

Before down voting this question. What else information do you need? I really don't know how to isolate this fault. If it occurs because of the embedded device, or rapidjson. And how to solve it.

========================== UPDATE ========================================

What is the device? http://www.keith-koep.com/de/produkte/produkte-trizeps/trizeps-iv-m-eigenschaften/

Does it have a hardware FPU? It is ARMv5 so I don't think so.

What compiler and libraries are you using (version numbers/specific builds)? What options are you passing to the compiler and linker?

arm-linux-gnueabi-g++ -march=armv5te -marm -mthumb-interwork --sysroot=/usr/local/oecore-x86_64/sysroots/armv5te-linux-gnueabi

Solution

  • This looks like it might be an undefined-behaviour-type bug in RapidJSON.

    Since you're targeting ARMv5, you're probably using a software floating-point library using the legacy ARM FPA format (as opposed to the later VFP, which uses IEEE754 format). Crucially, the FPA stores things in a weird middle-endian format, where 64-bit doubles are stored as two little-endian words, but most-significant word first.

    (Yes, big-endian ARM is a whole other complicated issue, but I'm deliberately ignoring it here since I don't see an armeb-* triplet or the -mbig-endian option anywhere)

    Consider 12.025 as an IEEE754 double:

    64-bit value:             0x40280ccccccccccd.
    little-endian byte order: cd cc cc cc cc 0c 28 40
    as little-endian words:   0xcccccccd 0x40280ccc
    

    Now in FPA format that would be:

    as little-endian words:   0x40280ccc 0xcccccccd
    byte order:               cc 0c 28 40 cd cc cc cc
    

    Trying to interpret that as a pure little-endian 64-bit value yields 0xcccccccd40280ccc, which just so happens to be the IEEE754 representation of -9.255965e+61. Fancy that!


    From a quick look around the code, it may strictly be more of an incompatibility than a bug, since RapidJSON does seem to explicitly assume IEEE754 format for floating-point values. To its credit, even though the parsing code looks pretty hairy, I do see unions rather than type-punning pointers. However, even if it's not relying on undefined behaviour, it's still relying on implementation-defined behaviour (the format of floating-point types), and unfortunately this compiler's implementation doesn't match that expectation.