Skip to content

Should prefer IV when both IV and NV are OK #39

@vphantom

Description

@vphantom

Hello,

After a lot of digging, I finally managed to understand why this module in my test script was always assuming that integers were huge float64's. Here's my discovery:

use Devel::Peek;
use Data::MessagePack;
my $mp = new Data::MessagePack;
$mp->prefer_integer(1);

my $a = [ 30000, 123, 300, 1000, 1000, 500, 500 ];

print 'Clean: ', unpack('H*', $mp->pack($a)), "\n";
#print Dump($a);
my $b = pack('wwwwwww', @{ $a });
#print Dump($a);
print 'After pack(): ', unpack('H*', $mp->pack($a)), "\n";

This outputs:

Clean: 97cd75307bcd012ccd03e8cd03e8cd01f4cd01f4
After pack(): 97cb40dd4c0000000000cb405ec00000000000cb4072c00000000000cb408f400000000000cb408f400000000000cb407f400000000000cb407f400000000000

Using Devel::Peek's Dump() I finally figured out that pack() does its reading in a way which triggers Perl to add an NV in addition to the original IV. Before pack() the first value is:

SV = IV(0x17d32b0) at 0x17d32c0
  REFCNT = 1
  FLAGS = (PADMY,IOK,pIOK)
  IV = 30000

But after pack() reads it, it becomes:

SV = PVNV(0x1934710) at 0x17d32c0
  REFCNT = 1
  FLAGS = (PADMY,IOK,NOK,pIOK,pNOK)
  IV = 30000
  NV = 30000
  PV = 0

My understanding is that the integer version of the value is still usable, but MessagePack prioritizes the float version. My first attempt at fixing that (PP.pm#274 and pack.c#193) broke test 21 "dirty float" so there seems to be more to Perl's IOK/NOK than meets my eye.

I'll experiment further and hopefully come up with a pull request. I figure there must be an efficient way to determine whether the IV loses precision vs the NV. If it doesn't then IV wins, if it does then NV wins.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions