C# Variables: float, double, and decimal

 C# Variables: float, double, and decimal

C# provides three common types for storing numbers with fractional values: float, double, and decimal. The correct choice depends on the required precision, memory usage, performance, and whether the value represents money or another human-facing quantity.



The core difference

The core difference is how they represent fractions:


  • float and double are binary floating-point (base-2) — they approximate decimal fractions

  • decimal is base-10 — it stores decimal fractions exactly

float

  • Size: 4 bytes

  • Precision: Approximately 7 digits

  • Range: Approximately ±3.4 × 10³⁸

  • Base: Binary

  • Stores 0.1 exactly? No

  • Suffix: f

  • Use for: Graphics, games, and large arrays

double

  • Size: 8 bytes

  • Precision: Approximately 15–17 digits

  • Range: Approximately ±1.7 × 10³⁰⁸

  • Base: Binary

  • Stores 0.1 exactly? No

  • Suffix: Default; no suffix is required

  • Use for: General mathematics and science

decimal

  • Size: 16 bytes

  • Precision: 28–29 digits

  • Range: Approximately ±7.9 × 10²⁸

  • Base: Decimal

  • Stores 0.1 exactly? Yes

  • Suffix: m

  • Use for: Money, finance, and user-facing numbers


Why it matters — the classic example:


double a = 0.1 + 0.2;

Console.WriteLine(a == 0.3);   // False!  (0.30000000000000004)


decimal b = 0.1m + 0.2m;

Console.WriteLine(b == 0.3m);  // True


float and double can't represent 0.1 exactly in binary (same reason you can't write ⅓ in base-10). The tiny error accumulates with every operation. decimal avoids this entirely because it works in base-10.


Rule of thumb:


  • Need speed and don't care about the last digit → double (it's the default)

  • Storing millions of values where memory matters → float

  • Calculating money or anything a human will read → decimal

Declaring the variables

A suffix tells C# which type a numeric literal should use:


float gameSpeed = 4.75f;

double distance = 149_600_000.0;

decimal productPrice = 19.99m;


A real-number literal without a suffix is a double. Therefore, this code is invalid:


// float value = 3.5; // Error: 3.5 is a double literal

float value = 3.5f;   // Correct


The f suffix creates a float, while the m suffix creates a decimal. The double type is the default for unsuffixed fractional literals.

Usage and advantages

float

Use float when memory usage is important and extreme precision is unnecessary. It is common in games, 3D graphics, animation, colors, sensor data, and very large numerical arrays.


float x = 10.25f;

float y = 3.5f;

float total = x + y;


Its main advantage is that it uses only 4 bytes per value. Its disadvantage is lower precision and a greater chance of rounding error.

double

Use double for general mathematical, scientific, engineering, statistical, and geometric calculations.


double radius = 6.5;

double area = Math.PI * radius * radius;


double provides much more precision than float and supports a very large range of values. It is usually the best default for approximate calculations. However, it should not normally be used for financial calculations because binary rounding can produce unexpected results.

decimal

Use decimal for prices, invoices, account balances, taxes, interest rates, and other values where decimal precision is important.


decimal price = 89.95m;

decimal taxRate = 0.20m;

decimal total = price + price * taxRate;


Console.WriteLine($"Total: {total:C2}");


decimal provides high precision for decimal fractions, but it uses more memory than float or double and is generally less suitable for high-performance scientific or graphics calculations.

Variable scope

The scope of a variable determines where it can be accessed. The rules are the same for float, double, and decimal.


A variable declared inside a method is a local variable. It can be used only within its containing block:


static void CalculateArea(double radius)

{

    double area = Math.PI * radius * radius;

    Console.WriteLine(area);

}


A variable declared inside an if statement or loop has a narrower scope:


static void CalculateTotal(decimal price)

{

    decimal total = price;


    if (price > 0m)

    {

        decimal tax = price * 0.20m;

        total += tax;

        Console.WriteLine(tax);

    }


    Console.WriteLine(total);

    // Console.WriteLine(tax); // Error: tax is out of scope

}


Parameters, such as radius and price, exist during the method call. Fields belong to an object or class and can be used by multiple methods according to their access modifier.

Conversions and comparisons

A float can be converted to double implicitly, but conversions involving decimal usually need an explicit cast:


float smallValue = 1.5f;

double largerValue = smallValue;


decimal price = 12.50m;

double rate = 1.2;

decimal total = price * (decimal)rate;


Conversions may lose precision, so they should be intentional. Also, use tolerance-based comparison for calculated float and double values:


double first = 0.1 + 0.2;

double second = 0.3;


bool nearlyEqual = Math.Abs(first - second) < 1e-12;

Final recommendation

Choose the type based on the meaning of the data:


  • Choose float for compact storage and large collections of approximate values.

  • Choose double for most general-purpose and scientific calculations.

  • Choose decimal for money and decimal quantities that users expect to calculate accurately.


Keep calculations in one numeric type whenever possible, add the correct literal suffix, and avoid using direct equality with calculated float or double values.

References

[1] Floating-point numeric types - C# reference

[2] Variables - C# language specification

[3] Built-in numeric conversions - C# reference



Comments

Popular posts from this blog

Callback function in JavaScript

Using Generic in TypeScript about types in the Code

Bitwise Operators in C/C++