Convert decimal to fraction to nearest 16th

Is there a simple way to change a float variable that changes based on an encoder position to a fraction to the nearest 1/16”? Example: float equals 4.264. I want to display the number as 4 1/4. Because the decimal when converted to a fraction is closer to 1/4 than it is 5/16. I was hoping there was a simple solution…. Any ideas?

Thanks all!

to calculate fraction to the nearest 1/16 :

float f = 0.264;
uint8_t fraction = ((uint8_t)(f*32 +1))/2;

it give you rounded result 4 for 0.264 (4.2 times of 1/16 part)

but for 0.288 (4.6 times of 1/16) - it give you 5

note: strip the integer part of your f value before calculating the fraction

1 Like

Thanks!!! I am still a little confused. I think I am following part of it, but I can't seem to grasp it all. Sorry.

In the equation above, I can't figure out what the uint8_t is for after the = sign: The bold part below:

If I put numbers in for the variables, I can't get it to come up to 4.2....

Ex. fraction = (.264 * 32 + 1) / 2
This gives me (8.448 + 1) / 2
9.448 / 2 = 4.724

I would think this would round to 5 for 5/16. What am I doing wrong?

Thanks alot!

Rounding is done by adding one in the formula.
So you should see to result BEFORE addition, 8.448/ 2= 4.224
This will be rounded to 4

It is called a cast operator and it says to the compiler "make what followed this to be of this variable type for the rest of this line of code only"

"to the end of the line" -you are confused with the comments.
Casting only applies to the variable immediately after it, in this case the contents of the parentheses

(uint8_t)(f*32 +1)

I am really about to show my stupidity.... Because of Order of Operations, shouldn't the 1 be added (to make it 9.448 then divided by two because it is out of the parentheses) before you divide by 2?

I know I am wrong, but I don't understand why.

Thanks!

yes, of course

Sorry..... So I don't understand how this can be:

Thanks!

Why not a full sketch to try ?

Can someone explain what I have been doing, because I not sure about it:

// For: https://forum.arduino.cc/t/convert-decimal-to-fraction-to-nearest-16th/1023040
// 
// Convert a float number to the nearest 1/16 and display it as a fraction
//
// I put the floating point number between 0 and 5.

void setup() 
{
  Serial.begin(115200);
  Serial.println( "Turn the knob");
}

void loop() 
{
  // create the input float number, between 0 and 5.
  float f = (float) analogRead( A0) / 1024.0 * 5.0;

  // multiply it by 16 to aim for a whole number for the 1/16 steps.
  f *= 16.0;

  // round it
  f += 0.5;

  // create the whole number
  long l = (long) f;

  // get the integer part of the original number
  int t1 = (int) (l / 16);
  Serial.print( t1);
  Serial.print( " ");

  // get the second part of 0...15
  int t2 = (int) (l % 16);

  // Solve printing it as a fraction
  switch( t2)
  {
    case 0: 
      break;
    case 1:
      Serial.print( "1/16"); 
      break;
    case 2: 
      Serial.print( "1/8"); 
      break;
    case 3: 
      Serial.print( "3/16"); 
      break;
    case 4: 
      Serial.print( "1/4"); 
      break;
    case 5: 
      Serial.print( "5/16"); 
      break;
    case 6: 
      Serial.print( "3/8"); 
      break;
    case 7: 
      Serial.print( "7/16"); 
      break;
    case 8: 
      Serial.print( "1/2"); 
      break;
    case 9: 
      Serial.print( "9/16"); 
      break;
    case 10: 
      Serial.print( "5/8"); 
      break;
    case 11: 
      Serial.print( "11/16"); 
      break;
    case 12: 
      Serial.print( "3/4"); 
      break;
    case 13: 
      Serial.print( "13/16"); 
      break;
    case 14: 
      Serial.print( "7/8"); 
      break;
    case 15: 
      Serial.print( "15/16"); 
      break;
  }
  Serial.println();

  delay( 500); 
}

The sketch in Wokwi: https://wokwi.com/projects/340244290635563603

2 Likes

you help newbies :slight_smile:

This makes sense... I haven't tried it, but I can see how it would work. Have you tried it? I appreciate it!

@b707 can you please give me an explanation to the question above?

Thanks!

which question?

I don't understand this if the order of operations are done, the addition should be done before the divided by 2.....: Post # 6

Here we have a misunderstanding.
When I said about "...before additions" I explained why 0.268 give you 4/16 and 0.288 - 5/16

Multiplication by 32 and addition of 1 is equivalent to @Koepel multiplication by 16 and addition of 0.5. I just get doubled operands to use integers and avoid of float math.

Addition of 0.5 is frequently used way to round float before convert it to integer. Because the casting float to int just discards the fractional part, without the addition you will give 9 either from 9.2 and 9.9. But after 0.5 supplement the first give you 9.7 and converts to 9 and the second will be 10.4 and rounded to 10

Hi, @neketege88

Can you tell us what your project is please?

What sort of encoder do you have?
Can you post link to specs/data?

Why do you need to display your values in non decimal fractions?
Surely 4.264 is closer to your measurement than converting it to an approximate?
It worries me that you are using inches, what is wrong with mm or cm?

Thanks.. Tom... :smiley: :+1: :coffee: :australia:

@b707

I am running this simple sketch:

float f; 
uint8_t fraction; 


void setup() {
  Serial.begin(9600);
  f = 0.264;
}

void loop() {
  
  f = f +.001;
 fraction = ((uint8_t)(f*32 +1))/2;
 Serial.println(fraction);
 Serial.println(f);
 

 delay(1000);
}

I know that it works and gives the value that you are expecting and that is correct.

What I can't figure out is how it is taking the f value and multiplying it by 32 THEN dividing by 2 before it adds the 1 that is in the parentheses. It seems to me it should...

.264 * 32 = 8.448 THEN + 1 THEN / 2 Which gives you 4.724 which rounds to 5

It is doing this:

.264 *32 = 8.448 THEN / 2 which gives you the correct 4.224 which rounds to 4 Where does the 1 come in at?

I just can't seem to get it.... I appologize!

@TomGeorge this is going on a sawmill project that has a lengthy thread on here but has been closed. I will add a link to it. Homemade bandsaw sawmill arduino controlled lift - #1160 by cattledog

I have added a few things to it since it closed. A lcd screen to readout the sawmill head location ia what I want to display the fraction on. I have it displaying to 3 decimal places now, but I don’t really need to be that precise. If I want to cut a 3/4 inch board it would be easier for me to see that it is on “12 - 1/2” and I need to move to “11 - 3/4” instead of 12.509 and trying to figure out where I would need to be. As for inches instead of metric. As you can see from my posts, I’m a dumb American I’m not smart enough to figure out metric!

Thanks!

Another take:

void printAsFract(float f);

void setup() {
  Serial.begin(115200);
  delay(1000);

  printAsFract(4.26);
  Serial.println();
  printAsFract(4.28125);
  Serial.println();
  printAsFract(0);
  Serial.println();
  printAsFract(.25);
  Serial.println();
  printAsFract(7);
  Serial.println();
  printAsFract(25.8125);
  Serial.println();
}

void loop() {
}

void printAsFract(float f) {
  const float roundingAmount = 1.0 / 32.0;

  Serial.print(f, 4);
  Serial.print(" --> ");
  f += roundingAmount;
  uint32_t integerPart = f;
  uint32_t fractPart = f * 16.0;
  fractPart &= 15;

  if ((integerPart > 0) || (fractPart == 0)) {
    Serial.print(integerPart);
    if (fractPart > 0) {
      Serial.print(" ");
    }
  }

  switch (fractPart) {
    case 0:
      break;

    case 2:
      Serial.print("1/8");
      break;

    case 4:
      Serial.print("1/4");
      break;

    case 6:
      Serial.print("3/8");
      break;

    case 8:
      Serial.print("1/2");
      break;

    case 10:
      Serial.print("5/8");
      break;

    case 12:
      Serial.print("3/4");
      break;

    case 14:
      Serial.print("7/8");
      break;

    default:
      Serial.print(fractPart);
      Serial.print("/16");
  }
}
4.2600 --> 4 1/4
4.2813 --> 4 5/16
0.0000 --> 0
0.2500 --> 1/4
7.0000 --> 7
25.8125 --> 25 13/16
2 Likes

Thanks all! I think I have a grasp on the concept. @b707, I apologize I couldn't understand why the mathematical calculation:
uint8_t fraction = ((uint8_t)(f*32 +1))/2;

did not round to the nearest whole number for example if the calculation above came up to 4.76, I expected that value to be a 5 when apparently it is not. 4.76 when stored in a uint8_t variable is a 4 not a 5..... That blew my mind.

@gfvalvo I follow your entire code fairly easily, but couldn't figure out this line:

I am still not sure how you knew to use this? Why choose 15? Because we want it broken in to 16 parts?

Thanks all!

The number 15 is in binary 00001111 in other words it consists of 4 bits. In 4 bits you can express 16 different bit patterns which in decimal is the numbers 0 to 15, so it is splitting the number into 16 parts.

The and operation & makes sure that any number can not exceed the value of 15. Which is what you want.

1 Like