Elektrine lite

← Feed

@simontatham@hachyderm.io

Post #996532

2026-03-31 09:49 UTC

A research problem I wonder if anyone has tackled: A tool like a disassembler must print integer constants that appear in its input. It must choose whether to print them in decimal or in hex. It's likely that some of those constants will be easier to make sense of when written in hex, and others will be clearer in decimal, depending what each constant is used for. E.g. printing "0x80000000" is nicer than printing "2147483648", but printing "1000000000" is nicer than printing "0x3b9aca00". So I've always thought it would be interesting to try to judge _per constant_ which base is more sensible to display it in. It would be easy enough to spot cases with small Hamming weight: print 0x1000000 or 0x80000001 in hex, print 1000000 or 5000001 in decimal. Considering very _large_ Hamming weight as well allows you to spot that 0x7FFFFFFC or 99999997 ought to be in hex or decimal respectively. But patterned bit masks like 0x55555555, or patterned decimal example numbers like 12345678, are more difficult to think of a simple rule for!

Replies (11)

  • @simontatham@hachyderm.io I suppose you want a histogram of the digits to be very spiky or very flat, but I've no idea how you'd express that as an algorithm.

    Open ##1098415

  • @KeyJ@mastodon.gamedev.place 2026-03-31 10:20

    @simontatham Convert to both, and print the one with the lowest entropy?

    Open ##2104111

  • @simontatham In disassemblers I often either see them both or see them based on the operation semantic. A flag is always displayed in hex but the argument to arithmetic operations is often in decimal. And I think it is generally undecidable. For example, 0×FFF is certainly simpler than 4095 but sometimes that number may make more sense.

    Open ##2104114

  • @bellinghman@wandering.shop 2026-03-31 11:14

    @simontatham Go old school: octal!

    Open ##2104115

  • @KeyJ@mastodon.gamedev.place 2026-03-31 11:32

    @simontatham My attempt at a 32-bit formatter: def fmt(x): x &= 0xFFFFFFFF r = [str(x)] if x >= 16: r.append(hex(x)) if x >= 1 << 31: x -= 1 << 32 r.append(str(x)) if x <= -16: r.append(hex(x)) return min(r, key=lambda x: (len(set(x.replace("0x","x"))), len(x.strip("-")))) for x in (0, 17, 257, 4097, 9999994, 100002, 0x7FFFFD, 0x55555, -294967296, -1, -10, -100, -0x555, 1<<29): print(fmt(x)) Lots of weird heuristics though, still passes neither 12345678 nor 0xDEADBEEF.

    Open ##2104118

  • @migratory@jorts.horse 2026-03-31 13:06

    @simontatham delta coding digits followed by RLE should give a pretty good simplicity metric

    Open ##2104119

  • @dan@axillae.telent.net 2026-03-31 10:06

    @simontatham and even then you might have to hardcode 0xdeadbeef and 0xcafebabe

    Open ##2104121

  • @jerrej@mastodon.social 2026-03-31 14:06

    @simontatham Maybe a handful of interesting mathematical sequences could help here? https://oeis.org/

    Open ##2104134

  • @f4grx@chaos.social 2026-03-31 18:29

    @simontatham for b in [8,10,16]: count zeros, select representation with most zeros.

    Open ##2104141

  • @bojidar_bg@mastodon.social 2026-03-31 19:41

    @simontatham Just to make matters more complicated (and lend extra weight to the "always show both" suggestion): sometimes, constants make sense only in relation to other constants - for example, 255 is arguably best written as hex; but if you have a list of multiples of 5 between 0 and 300, seeing [ ..., 250, 0xff, 260, ... ] will confuse the human reader.

    Open ##2104143

  • @jackv@mas.to 2026-03-31 20:28

    @simontatham at first I didn't notice you said disassembler. I was thinking a programming language could use a different variable for for constants originally expressed as dec/hex. I think "print both" was my favourite suggestion. Occasionally it will be informative when you think "oh!" But it's only practical if there's not too many

    Open ##2104147