0

I have decimal numbers in the range between 0 and ~15.9 and want to convert them into a specific hexadecimal notation. So the special notation goes like this: There are 16 bits. The first 4 bits are for the pre-decimal-point position. The next 12 bits are for the decimal places.

A * 16^0 + B * 16^{-1} + C * 16^{-2} + D * 16^{-3} Image of previous equation

Here is how I am doing it now in C++ (I am "brute forcing" it. Just check how often 16 fits in A, then check how often 1/16 fits in B and so on):

uint16_t dec_to_special_hex(double x){
    uint16_t A, B, C, D;
    A = static_cast<uint16_t>(x);
    if (A > 15)
        return 65535;

    double remainder = x - A;    //get decimal places
    B = static_cast<uint16_t>(remainder * 16);
    remainder = remainder - B * (1 / 16);
    C = static_cast<uint16_t>(remainder * 16 * 16);
    remainder = remainder - C * (1 / (16 * 16));
    D = static_cast<uint16_t>(remainder * 16 * 16 * 16);
    remainder = remainder - D * (1 / (16 * 16 * 16));


    uint16_t temp, ret = A;
    ret = ret << 12;
    temp = B << 8;
    ret = ret | B;
    temp = C << 4;
    ret = ret | C;
    ret = ret | D;
    return ret;
}

I wounder if there is no better, more elegant way to do this. I am looking at the digits I am handling and it just feels like there must be more to it, but I could not find a better way yet. Would be happy for your suggestions!

Paul Floyd
  • 5,530
  • 5
  • 29
  • 43
MYZ
  • 331
  • 2
  • 10
  • 2
    Unless I'm missing something, your "special hex notation" is a 4+12 bit fixpoint number. Just do `return x>=16 ? 65535 : static_cast(x*(1<<12));` (probably also explicitly return 0 if `x<=0`, in case that ever happens) – chtz Jul 29 '20 at 07:29
  • wow :-) Thanks for the help. I did not realize that is a common notation. I will need to look up why this works exactly how it does but thank you! If you post it as an answer I will be happy to mark it as the correct one! – MYZ Jul 29 '20 at 07:42
  • Does this answer your question? [Numerical Conversion in C/C++](https://stackoverflow.com/questions/1179006/numerical-conversion-in-c-c) (Maybe someone finds a better duplicate ...) – chtz Jul 29 '20 at 07:53
  • 1
    Re “I have decimal numbers”: The input `x` is not a decimal number; it is a `double`. (A C++ implementation could use a decimal base for `double`, but no common implementations do; today’s all use binary.) People should avoid using “decimal” to mean “numbers with fractional parts” or “floating-point numbers” or “real numbers,” because this leads to the numerous questions we get about floating-point behavior where people are expecting it to behave like simple decimal arithmetic. It is not decimal. – Eric Postpischil Jul 29 '20 at 12:00

2 Answers2

1

I wounder if there is no better, more elegant way to do this

Elegant is subjective, but:

One thing you can do is work with the bits directly. Check this simple converter to see how IEEE floating point numbers work (if you don't know already). Then look at this question for a method to get the different groups of bits of a floating point number (the answer therein is for a float type, but the conversion to double is simple).

After you have the bits, All you need to do is shift the mantissa, depending on the exponent, and perform bit-wise & with different windows to get the bits for A, B, C and D.

Benny K
  • 868
  • 6
  • 18
  • Thank you for your answer and the links. I had worries about the accuracy but as you said, in the end I am only going to end up with a 16 bit number and precision is not my main concern. – MYZ Jul 29 '20 at 09:00
  • Re “performing multiple arithmetic operations … reduces accuracy”: If the C++ implementation uses a binary-based format for `double`, as all common C++ implementations do, and the input `x` is not out of bounds for the `uint16_t`, then there will be no arithmetic errors in the code shown. The conversion to `uint16_t` truncates, producing on exact result in `A`. The subtraction of `A` produces an exact result by Sterbenz Lemma. The multiplication by 16 produces an exact result since it is a power of the floating-point base. And so on. – Eric Postpischil Jul 29 '20 at 10:51
  • @EricPostpischil You're right. I didn't notice this. Edited the answer. – Benny K Jul 29 '20 at 10:56
1

Assuming you just wish to truncate and not round, this can be done with:

uint16_t dec_to_special_hex(double x)
{
    return
        x < 0   ?     0 :
        16 <= x ? 65535 :
                  4096*x;
}
Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312