C# Data Types: Choose Values That Fit the Problem
A type determines which values a variable can represent and how operations behave. Start with what the information means, then choose a representation that fits its range, precision and treatment of missing data.
What you will understand
- Choose types for counts, identifiers, measurements, prices and flags.
- Distinguish an integer range from an application's allowed input range.
- Recognize the effect of numeric literal suffixes and inferred types.
- Explain why integer division can discard a fraction before assignment.
- Compare binary floating-point approximation with decimal arithmetic.
- Distinguish a char from a string and a missing value from zero.
- Identify compilation and runtime failures caused by unsuitable representations.
Choose a type from the meaning of the information
Begin by asking what the value represents. A count, a measurement, an identifier and a permission flag have different meanings even when their source literals look similar. Type choice determines which values can be stored and which operations are appropriate. It is more than choosing how a label will look in the console.
A member code such as 007 is a useful example. Arithmetic on it would usually have no meaning, and its leading zeros should not disappear. Store it as text. In contrast, a book count is something you may add or subtract, so an integer representation is appropriate within its range.
Use the examples as decisions you can explain rather than a list of keywords to memorize. Each editor contains a complete separate Program.cs. The previous lesson's declaration and assignment rules still apply: a chosen type is not replaced when you later give a variable a new value.
Program.cs · Choose by meaning
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Member: 007
Books: 3
Zone: B
Can borrow: True
The member code is text because its leading zeros are meaningful and you are not adding codes together. The book count is an integer, the simple zone marker fits one char, and permission is a Boolean. Merely printing Can borrow: True does not implement authorization: the flag is a source value in this example.
Whole-number types have limits; your domain has rules
int is a signed 32-bit integer, a useful starting point for many ordinary counts. long provides a signed 64-bit range when the expected values need it. byte represents values from 0 through 255 and is useful for byte-oriented data; selecting the smallest type by habit can make an ordinary count unnecessarily restrictive.
A representable value is not necessarily an allowed application value. Negative counts fit int, but a library may reject a negative number of borrowed books. Conversely, a legitimate large counter can exceed int's range. Choose a representation for the expected scale and enforce domain limits separately.
Inspect the bounds below rather than trying to memorize all digits. A literal that does not fit can be rejected during compilation. An arithmetic result can also go out of range while the program runs. Plan for the operation as well as the starting value.
Program.cs · Inspect whole-number limits
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
byte: 0 to 255
int: -2147483648 to 2147483647
long: -9223372036854775808 to 9223372036854775807
These are the representable bounds of three built-in types. They are not automatically the right business limits. An int can represent a negative book count, although your application may reject it. The MinValue and MaxValue names are library constants you can inspect instead of memorizing every digit.
Program.cs · A literal outside the chosen range
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Compilation fails: 256 is outside the byte range.The compiler can see that this constant value will not fit in byte. Choosing int for an ordinary count avoids this particular limit; it still does not validate the count against stock or another domain rule. Do not force a narrowing conversion simply to hide the range mismatch.
A wider destination does not rescue every intermediate result
The checked example explicitly requests overflow checking around one integer addition. It illustrates a runtime failure after an earlier message printed. Do not assume that every out-of-range operation is automatically diagnosed in the same way; constant expressions, checked contexts and unchecked arithmetic have different rules.
Represent the operation's inputs appropriately before it happens. A long result variable alone cannot undo overflow that already occurred in an int expression. The working example uses long for the counter itself, so its addition is performed in a representation capable of holding this particular result.
The important question is not whether a type is big, but whether the expected values and intermediate results fit. long is finite too. Detailed conversions are next in the course; here, learn to recognize that a representation problem cannot always be repaired by changing only the final display or destination.
Program.cs · Detect an overflowing integer operation
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Before: 2147483647
Then execution fails with OverflowException.The variable is at int's maximum. The explicit checked expression requests an overflow check for this addition, so execution fails rather than producing a valid larger int. The earlier line has already printed. Without an explicit checked context, integer overflow behavior depends on the operation and overflow-checking context; do not assume every addition throws automatically.
Program.cs · Use a suitable range before adding
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Count: 2147483648
The value and addition now use long, which can represent the result. The L suffix explicitly makes the literal a long; digit separators improve source readability without appearing in output. Choosing a larger type moves the limit, not removes it. A long also has a finite maximum.
Literal suffixes tell the compiler which representation you mean
For the literals used here, 12 is an int, 12L is a long, 12.0 is a double, 12.0m is a decimal and 12.0f is a float. The suffix belongs to source syntax and is not printed as part of the number. Prefer an uppercase L so it is easy to distinguish from the digit one.
When you declare with var, the initializer still establishes a compile-time type. The inspection example prints .NET type names to make that concrete. You do not need to use GetType in every report; it is a learning aid here. The familiar C# keywords are aliases for types with names such as Int32 and Decimal.
float uses less storage and generally provides less precision than double; some APIs require it. double is a common choice for many fractional measurements. Neither one is a synonym for any number. An ordinary decimal-point literal assigned to a decimal variable needs an appropriate decimal representation, rather than relying on automatic interchangeability.
Program.cs · Literal syntax affects inferred types
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Int32
Int64
Double
Decimal
Single
GetType().Name is used here only to inspect the inferred runtime type name. Int32 corresponds to int, Int64 to long, Double to double, Decimal to decimal and Single to float. var still has a fixed type. The suffix is part of the literal's source syntax; it is not part of the numeric value printed to a user.
Division and approximation happen before output formatting
Integer division produces an integer result, truncating a fractional remainder toward zero. A later assignment to double cannot bring that remainder back. If the required answer is fractional, the operands must cause an appropriate fractional operation before the result is stored.
Binary floating-point is a finite approximation system. Many common decimal fractions do not have exact finite binary representations. The equality example shows one consequence using ==, the equality operator: an arithmetic expression can look equivalent on paper without producing exactly the same stored double value.
This does not make double broken. Measurements are often approximate already, and a comparison strategy should match their scale and tolerance. Do not invent a single magic tolerance for every situation. This lesson identifies the representation issue; later numeric and application work develops the comparison policy.
Program.cs · The result type on the left cannot restore lost information
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Integer operands: 3
Fractional operand: 3.5
The first division has two int operands and produces the integer result 3, discarding the fractional part toward zero. Assigning that result to double produces 3, not 3.5. The second division uses a double operand, so the operation can produce a fractional result. This is about the operation's inputs, not just the declaration on its left.
Program.cs · Decimal fractions and binary approximation
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Double equals 0.3: False
Decimal equals 0.3m: True
The == operator asks whether the values are equal. These double operands approximate the decimal fractions in binary, and their sum does not exactly equal the double literal 0.3. The decimal calculation represents these particular decimal fractions exactly. This does not mean decimal represents every possible fraction exactly or that equality is always a suitable comparison strategy for measured values.
Use decimal deliberately, not as a promise of perfect arithmetic
decimal can exactly represent many base-10 fractions that double approximates, making it useful for many financial quantities. It still has finite range and precision. Fractions such as one third cannot be represented exactly with a finite decimal expansion either, and a real pricing system still needs currency, scale and rounding rules.
Keep related operands in a deliberate representation. The example uses a decimal unit price and an integer quantity. The mixed example instead combines decimal and double values, and the compiler rejects that addition. Prefer compatible declarations when the quantities have the same meaning rather than adding arbitrary casts to suppress an error.
F2 formats the output with two fractional digits; it does not turn the source into an accounting system. References below use the runner's decimal-point representation. A local terminal can use a comma or other culture-specific formatting without changing the numeric value. Compare the calculation separately from its presentation.
Program.cs · Keep decimal arithmetic consistent
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Subtotal: 37.50
The m suffix supplies a decimal literal. Multiplication by an int quantity is supported, and F2 requests two fractional digits in the display. It does not change the stored subtotal or define a complete currency policy. The reference uses a decimal point; a local terminal's culture can format that separator differently. Taxes, currencies, discounts and rounding policy are outside this report.
Program.cs · Do not mix decimal and double casually
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Compilation fails: this addition mixes decimal and double operands.There is no implicit common representation for this decimal-plus-double expression. If both values represent decimal charges in this example, declare both as decimal and use m literals. Conversions can be explicit in other situations, but their precision consequences need deliberate treatment; the next lesson covers that boundary.
Text and flags are not numbers wearing different labels
A string represents general text and uses double quotes for ordinary literals. A char literal uses single quotes and represents one UTF-16 code unit. A displayed symbol can require multiple code units, so the beginner shorthand one character is not a complete model for all Unicode text.
A quoted digit can look like its numeric counterpart when printed, but the types differ. Keep an identifier as text when spelling, leading zeros or nonnumeric characters matter. Parsing user text into numeric values is a deliberate boundary that appears in later lessons, not something an output label performs automatically.
bool has true and false values. The word true inside quotes is a string, and 1 is a numeric literal. Choose a Boolean for a yes-or-no state, then implement the actual policy that sets it. Declaring a flag does not by itself enforce an access rule or validate another field.
Program.cs · A digit can be text instead of a count
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Digit marker: 7
Member code: 007
Name: Mina
Single quotes introduce a char literal; double quotes introduce a string literal. The marker '7' is not the integer 7, even though they can look identical in output. A char is one UTF-16 code unit, so it is not a universal container for an entire visible symbol such as every emoji. Use string for general text, names and identifiers.
Represent absence separately from a legitimate zero
Some fields are not known yet. An int? value can hold an int or null, which represents absence. A missing score is not necessarily zero; zero might be a valid completed score. Choosing a nullable type preserves that distinction instead of silently replacing missing information with a real numeric value.
HasValue lets you inspect whether the underlying value exists. The example starts missing and then assigns a present zero. The Value property requires a present value and fails when none exists. Application code needs an explicit check or absence policy before that access. Avoid changing data semantics just to hide an exception.
Nullable value types are not a shortcut for reading unassigned locals. A local declaration still follows the definite-assignment rules from the previous lesson. Also distinguish int? from nullable-reference annotations such as string?: they relate to different type categories. You will study those categories further as the course introduces objects.
Program.cs · Separate missing from zero
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Before has value: False
After has value: True
Score: 0
int? supports the int values plus null. HasValue reports whether an actual int is present. A present score of zero and a missing score are different states. Interpolating a missing nullable value can produce empty text, so a blank message is not a reliable user-facing explanation; this example prints presence explicitly.
Program.cs · A missing nullable value has no Value to read
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Reading score
Then execution fails with InvalidOperationException.Value requires an underlying value to be present. Accessing it while score is null fails at runtime; the compiler may also warn about the access. Do not replace every missing score with zero to suppress this distinction. Use an explicit presence check or an appropriate absence policy before accessing the value in application code.
Guided repair: a fractional-looking answer with the wrong value
Seven minutes spread across two sessions should average 3.5 minutes. The original program reports 3.0 even though its result variable is double. Identify the types used by the division, then repair the representation before the operation takes place.
Program.cs · Repair an average with fractional output
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Average: 3.0 minutes
The formatted decimal digit hides the loss: the program prints 3.0, but the intended average is 3.5. The division happened with two integers before assignment to double. Formatting cannot reconstruct a fraction that was discarded during the calculation.
Repair checkpoints
- The original compiles and prints 3.0; formatting is not the underlying fix.
- Perform fractional division so the reference inputs print 3.5.
- With 8 minutes and 2 sessions, expect 4.0; with 1 minute and 2 sessions, expect 0.5.
- Do not hardcode 3.5 as the answer.
- The exercise assumes a positive session count; zero needs a separate validation policy.
Reveal the repair and explanation
Program.cs · Choose the operand representation deliberately
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Average: 3.5 minutes
The total is represented as a double before division, so a fractional result is available. This choice suits a duration that may include partial minutes. If an application's source total must remain an integer, a deliberate conversion before division is another solution; changing only the destination variable still would not solve the original error.
Independent lab: choose types for a workshop card
Build a workshop card using representations that fit each field's meaning. The starter runs, but it cannot preserve the required code or represent the fractional price correctly. Explain each type choice as well as checking the report.
Acceptance checklist
- Represent workshopCode as the exact text 007.
- Use an int seatCount of 12 and a decimal unitPrice of 14.50m.
- Represent registrationOpen with bool rather than the string true.
- Calculate the potential subtotal from price and count, then display it with two fractional digits.
- Print the four labelled lines shown in the reference solution.
- Keep input validation, registration policy and actual payments outside the claims of this fixed-input report.
Program.cs · Workshop card starter
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Workshop: 7
Seats: 12
Potential subtotal: 180
Registration open: true
The starter compiles, but its representations do not meet the lab: the code loses its meaningful leading zeros, the price cannot represent 14.50, and a text flag is not a Boolean. Choose types from the field meanings rather than judging only whether the sample output looks plausible.
Show hints
- Double quotes preserve the identifier's leading zeros.
- The m suffix supplies a decimal literal; an ordinary decimal-point literal is a double.
- A Boolean's standard printed representation starts with a capital letter. Text that resembles it is still a different type.
Reveal the reference solution
Program.cs · Workshop card solution
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Workshop: 007
Seats: 12
Potential subtotal: 174.00
Registration open: True
The identifier is preserved as text, seats are counted with int, the price uses a decimal literal, and the flag uses bool. The potential subtotal is a calculated report, not a received payment or a reservation. A suitable type still does not enforce nonnegative prices, available seats or permission to register.
Changed-input checks
- Zero seats with price 14.50m should produce subtotal 0.00.
- Three seats at 0.10m should produce subtotal 0.30.
- Two seats at 19.99m should produce subtotal 39.98.
- Change the code to A07 and registrationOpen to false; preserve the code and print False.
- A negative seat count still computes a negative subtotal here. Explain why the chosen types do not replace domain validation.
Recap: a starting point for choosing a representation
Use these choices as questions to investigate, not automatic guarantees. Check the domain’s range and precision requirements and the operations that will run before the final result is stored.
int- An ordinary whole-number count within its range. Add separate validation for allowed counts.
long- A whole-number value that needs a wider range than int. Check intermediate operations too.
double- A fractional measurement where binary approximation is appropriate. Compare measured values deliberately.
decimal- Base-10 quantities such as many financial amounts, with explicit units and rounding rules when needed.
bool- A true-or-false state. The string true and the number 1 are different types, not equivalent Boolean literals.
char / string- One UTF-16 code unit versus general text. Use text for codes whose leading zeros matter.
int?- A whole-number value that can also be absent. Missing is not the same state as a present zero.
Frequently asked questions
Is decimal always more accurate than double?
No single type is best for every purpose. decimal represents many decimal fractions exactly, while double is useful for many measurements and has different range and performance characteristics. Both have finite precision; neither exactly represents every real number.
Why does double result = 7 / 2 give 3?
Both operands are integers, so the division produces 3 before assignment. Converting that result to double does not restore the discarded fraction. Choose an appropriate fractional operand before division.
Should I use long for every count?
Choose by expected range, operations and API requirements. long provides a wider finite range than int, but it does not validate nonnegative counts or eliminate every possible overflow.
Is a char enough for any emoji?
No. char is one UTF-16 code unit. Some visible symbols require multiple code units, and some displayed text elements contain multiple Unicode code points. Use string for general text.
Can I use zero instead of null?
Only when the domain actually defines zero as the missing-value convention and every consumer understands it. Otherwise a valid zero and absence become indistinguishable. A nullable type can preserve both states explicitly.
Can you justify the representation?
Reason about meaning, intermediate operations, precision and missing values. Check each explanation after submitting.
Next step and references
A type gives a value a representation and a set of operations. Integer ranges are finite, fractional arithmetic depends on operand types, and nullable values preserve absence separately from real data. Correct formatting cannot repair lost precision or an inappropriate representation.
Next is C# Conversions and Type Casting. You will examine how to cross between representations deliberately and identify information that can be lost along the way.
Official references
Original SovranCode explanations, examples and exercises. Technical details were checked against these references.
- Microsoft Learn: Integral numeric types
- Microsoft Learn: Floating-point and decimal types
- Microsoft Learn: Nullable value types
- Microsoft Learn: char
Last updated: .