# BCD values for binary fractions

I am writing Verilog code to display fraction numbers on the Basys3's seven segment display in decimal. To set the LEDs to display the appropriate digits, I need a way to convert this fraction to a BCD values. For whole numbers, I could map every four bits to a number ranging from 0-9, and if I get a hex value exceeding 9, I can add 6 and have a carry-out of 1. However, I do not know how to deal with fractions, and could not find correct information online. Is there some algorithm I can use to code this?

Edit: The fraction to be converted is 16 bits long. The first bit furthest to the left is the sign bit, and the second bit next to it represents the non-fraction part.

There are four seven-segment displays available. All the numbers being dealt with are unsigned, so a negative sign does not need to be included on the display. The first display is used to store the non-fraction part (only one bit is provided, so the non-fraction part is either a zero or one).

The remaining displays are for the fraction part. That means I need to take a 14 bit binary fraction (16 bits - the two bits for sign and non-fraction part), and then find some way to map this to the corresponding decimal value to be displayed.

• Do you mean to display 1/7 as .1429? or 0.143? Commented Jul 18, 2023 at 21:15
• Sorry, I added more detail in my edit. Since I have three segments available for the fraction part, I would want to display 0.143. Commented Jul 18, 2023 at 21:27
• What would you do with 100/7? Do you float the decimal point around? Doesn't seem like it, reading. Just want to be sure. Commented Jul 18, 2023 at 21:28
• When you say float the decimal point around, do you mean normalize 100/7 to allow for one non-fraction bit? Commented Jul 18, 2023 at 21:33
• 100/7 is over 14. So in that case you would need to reserve two non-fractional digits. If you want to support that then you need a normalization step. It matters what you want to achieve. Commented Jul 18, 2023 at 21:34

## 1 Answer

I'll keep it short.

You wrote in comments, a few hours after your question was last edited, that "all the numbers being dealt with are unsigned" and that "all numbers when received are already in the 2.14 format (two bits to the left of the decimal point, remaining 14 to the right)".

Given that, I recommend ignoring the 2.14 format, per se, and instead treating the entire thing as a simple unsigned 16 bit integer.

Call this integer $$\N\$$.

## Compute $$\\left[\left(N\ll 7\right)-\left(N\ll 1\right)-N+0\text{x}400\right]\gg 11\$$

For example, using 0xDCC4 (2.14 format rounds to 3.449):

  110 1110 0110 0010 0000 0000 x128
- 000 0001 1011 1001 1000 1000 x2
- 000 0000 1101 1100 1100 0100 x1
+ 000 0000 0000 0100 0000 0000 (0x400)
===================================
110 1011 1100 1111 1011 0100
_______________^^^^^^^^^^^^^

RESULT = 1101 0111 1001 (decimal 3449)


For example, using 0xDCC5 (2.14 format rounds to 3.450):

  110 1110 0110 0010 1000 0000 x128
- 000 0001 1011 1001 1000 1010 x2
- 000 0000 1101 1100 1100 0101 x1
+ 000 0000 0000 0100 0000 0000 (0x400)
===================================
110 1011 1101 0000 0011 0001
_______________^^^^^^^^^^^^^

RESULT = 1101 0111 1010 (decimal 3450)


Corner case (maximum value should result in integer 4000 to make 4.000):

  111 1111 1111 1111 1000 0000 x128
- 000 0001 1111 1111 1111 1110 x2
- 000 0000 1111 1111 1111 1111 x1
+ 000 0000 0000 0100 0000 0000 (0x400)
===================================
111 1101 0000 0011 1000 0011
_______________^^^^^^^^^^^^^

RESULT = 1111 1010 0000 (decimal 4000)


In each of these cases, you take the upper 12 bits and toss away the lower 11 bits.

## Algorithm comments

Note that you want to multiply by 1000 to get an integer. But since you want to round, you need to multiply by 2000, instead. Then add 1 to that and shift it back down by one bit, dropping the rest of the right side.

The equation I gave you, INT((2000*N/16384+1)/2), though isn't really coded up for binary integer addition and subtraction. To fix that, just multiply the interior by 16384 and compensate to now get INT((2000*N+16384)/32768).

Put another way: $$\\frac{\text{0x7D0}\,\cdot\, N + \text{0x4000}}{\text{0x8000}}\$$.

Still, notice that the hex constants I'm using all have '0' on the right? That's four bits we don't need to care about. Once those are dropped, one of the constants then has a '1' as its least significant bit (0x7D), so nothing more to do.

\begin{align*} &&\frac{\text{0x7D0}\,\cdot\, N + \text{0x4000}}{\text{0x8000}} \\\\ \text{perform }\gg 4&\to&\frac{\text{0x7D}\,\cdot\, N + \text{0x400}}{\text{0x800}} \\\\ \text{0x7D=0x80-0x2-0x1}&\to&\frac{\text{0x80}\,\cdot\, N -\text{0x2}\,\cdot\, N-N+ \text{0x400}}{\text{0x800}} \\\\ \text{convert to shifts}&\to&\left[N\ll 7-N\ll 1-N+ \text{0x400}\right]\gg 11 \end{align*}

The division/shift-by-11 really just means throwing away the bottom 11 bits.

Now just use the usual integer BCD code. You know where to always put the decimal point, too.

You can brute-force it as shown above and use 23-bit operations. But further optimization is available. Take a careful look at what's going on in operations related to the lower order 7 bits, for example. You can avoid anything more than 16-bit operations for that reason alone. But I'll leave that and other ideas as an exercise in detail.

## BCD -- consider also double dabble

Finally, you can use the double dabble algorithm to convert from binary to BCD for output. In the following, each block represents one double-dabble step:

That's a combinatorial approach.

Hopefully this helps.

• How do you take the sign bit into account? Commented Jul 19, 2023 at 6:29
• @thebusybee There's no need to do so. Did you not read the question? "All the numbers being dealt with are unsigned..." Commented Jul 19, 2023 at 8:55
• Well, in the question the OP says "The first bit furthest to the left is the sign bit", and in a comment "but actually the first bit is a sign bit". Granted, there is also the statement "All the numbers being dealt with are unsigned". Anyway, only 15 bits define the magnitude as a 1.14 number. Your examples use 2.14. Commented Jul 19, 2023 at 9:08
• @thebusybee They do, as the OP says 2.14, as well. See: "all numbers when received are already in the 2.14 format (two bits to the left of the decimal point, remaining 14 to the right)" in comments. That said, it's not at all difficult to make adjustments for sign, were it necessary. Trivial, actually. Commented Jul 19, 2023 at 9:31
• @thebusybee The question author made their comments hours after their last edit of the question. So I take them as over-riding anything seen in the question. I have added a paragraph to help avoid still further confusion about why and/or what I'm addressing. Thanks for making me realize this necessary clarifying addition. Appreciated. Commented Jul 19, 2023 at 9:53