C# Conversions and Type Casting: Change Types Deliberately
A conversion expresses a value using another type. Choose when it happens and what information must survive: a cast can discard a fraction, a smaller integer can overflow, and numeric text needs parsing rather than a numeric cast.
What you will understand
- Explain an implicit conversion without claiming that every automatic numeric conversion is exact.
- Read cast syntax and distinguish a converted result from a changed source variable.
- Compare truncation toward zero with midpoint-to-even rounding.
- Use checked to detect an out-of-range numeric cast.
- Distinguish numeric casting, parsing text and formatting output.
- Place a conversion before integer division when a fractional answer is needed.
- Test changed inputs and justify the information a conversion keeps or discards.
A conversion creates a value in another representation
You already know how to choose a type. Now imagine that one part of a program counts visits with int, while another stores totals with long. A conversion lets a value cross that boundary. The question is whether the destination can represent the information you intend to keep.
Think of copying a measurement into a report with different precision. Sometimes the copy can preserve everything; sometimes the destination cannot show the fraction or distinguish nearby large numbers. A conversion is a decision about information, not merely a way to silence a compiler message.
You need variables, assignments and the basic numeric types from the previous lessons. Each editor here is a complete separate Program.cs. Predict the result first, select Run, then compare actual execution with the reference. Two examples intentionally fail compilation and one intentionally fails at runtime.
Program.cs · Copy into a wider integer representation
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Daily: 120
Total: 120
Int32
Int64
The assignment converts the copied int value into long. The source variable remains int and keeps its value. long can represent every int value, so no cast is needed for this conversion. The two .NET type names make the difference visible even though both numbers print as 120.
Read a cast from right to left: value, then target type
An implicit conversion uses ordinary assignment syntax because C# permits that conversion without a cast. int to long is a useful starting example: the destination can hold every int. Do not memorize an informal ladder claiming that every larger-looking numeric type is automatically exact.
For an explicit numeric conversion, write the destination type in parentheses before the expression: (int)distance. Read distance first, then ask what representing that value as int would do. The cast produces a result; it does not redeclare the source variable or mutate its stored value.
The missing-cast example fails before any output is produced. The repaired form below runs, but its changed value is part of the behavior you must understand. An explicit cast authorizes a supported conversion; it does not guarantee an unchanged value or a successful result for every input.
Program.cs · An explicit conversion is required
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Compilation fails: double cannot be assigned to int implicitly.A double may have a fraction or a value outside the int range. The compiler requires an explicit conversion here. This diagnostic is a request to decide what the conversion should mean, rather than a request to add parentheses blindly.
Program.cs · A cast truncates toward zero
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Forward: 12
Backward: -12
Original: 12.8
Read (int)forward as convert the current value of forward to int. For these in-range values the fractional part is discarded toward zero. The negative value becomes -12, not -13, so this is not mathematical floor. The original double is unchanged; the cast creates a converted result.
Truncation and rounding answer different questions
Converting an in-range fractional number to an integer with a cast removes its fractional part toward zero. Positive 12.8 becomes 12 and negative -12.8 becomes -12. That is different from rounding to the nearest integer and different from always moving downward on the number line.
Convert.ToInt32 has different overloads for different source types. With a double argument it rounds to the nearest integer, choosing the even result when the value is exactly halfway. The method name alone is not enough: inspect the source type and the required policy.
For a report of complete units, truncation may be the stated requirement. For a measured estimate, another rounding rule might be appropriate. There is no universal correct rounding rule. Write down the requirement and test midpoint and negative values rather than changing an API until one example looks right.
Program.cs · Compare truncation with a rounding method
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Cast 2.5: 2
Convert 2.5: 2
Convert 3.5: 4
Convert.ToInt32(double) rounds to the nearest integer and sends an exact midpoint to the even integer. That is a different policy from a cast, which truncates. The pair 2.5 and 3.5 is deliberate: testing only 2.5 would make the policies look identical. Both still require a result within int range.
Use checked when an out-of-range integer conversion must fail
A byte can hold 255 but cannot hold 300. Adding a cast does not expand its range. The checked expression below makes the mismatch visible as OverflowException. It is a runtime error because the cast reads a variable while the program executes.
The unchecked comparison deliberately demonstrates bit loss in an integral conversion. Keeping only the low eight bits of 300 yields 44. That is neither the original reading nor a clamp to the maximum. Usually, a reading that legitimately reaches 300 should stay in an appropriate wider type.
checked is not general validation. It does not reject a negative int that is within range, restore fractional digits, or prove a value is allowed by your application. It also does not make every arithmetic operation checked everywhere. Here its parentheses clearly identify the conversion being checked.
Program.cs · Detect a narrowing value outside its range
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Reading: 300
Then execution fails with OverflowException.byte holds 0 through 255. The explicit checked expression rejects converting 300 to byte at runtime. The first line has already printed; the final line never runs. Use a wider representation when the real data needs one, rather than interpreting failure as a reason to discard information.
Program.cs · See what an unchecked integer cast discards
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Original: 300
Converted: 44
This explicit unchecked integral conversion keeps only the low eight bits. For this positive example, 300 modulo 256 is 44. It does not clamp to 255 and does not preserve the reading. This demonstrates integer bit truncation; do not extrapolate that wraparound rule to out-of-range double-to-int conversions.
Range and exactness are separate requirements
A double has a very large range but finite significant precision. Near sufficiently large integer values, neighboring whole numbers cannot all be represented distinctly. Some implicit numeric conversions therefore lose precision even though their numeric magnitude fits.
The large-counter experiment converts long to double and back. The missing unit was lost during the first conversion, so casting back cannot recover it. This is why an exact counter or identifier should not take an unnecessary trip through a floating-point representation.
decimal and double do not have an implicit conversion between them. If an API supplies double while your calculation uses decimal, make the boundary explicit and examine its precision and range. Better source representations prevent losses that a later conversion cannot reliably reverse.
Program.cs · Automatic does not always mean exact
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Original: 9007199254740993
Restored: 9007199254740992
Equal: False
long-to-double is allowed implicitly, but double cannot distinguish every integer at this size. The return cast exposes the lost unit. A large enough numeric range is different from enough precision for every integer. Keep exact identifiers and counters in an appropriate exact representation.
Program.cs · Cross a numeric boundary deliberately
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Total: 13.25
Here the external fee is deliberately converted to decimal before addition. This particular 0.75 value converts cleanly; that is not a guarantee for every double value. Prefer decimal at the source when all amounts are decimal quantities. Casting later cannot reconstruct original information that was already lost.
Parse text; do not cast its spelling
The string "12" contains characters. A numeric cast does not read those characters as a decimal number, so (int)seatText fails compilation. Parsing is the separate operation that interprets numeric text using rules for the requested numeric type.
int.Parse is concise for known valid text, but invalid text can throw FormatException and an out-of-range number can throw OverflowException. int.TryParse gives a success flag instead for these ordinary failures. Its out parameter receives the result; this is your first preview of that syntax, not a requirement to master method parameters today.
In the TryParse experiment, a failed result slot is zero. A legitimate input of "0" also yields zero, but succeeds. Always keep the success flag. Parsing success also does not prove domain validity: "-2" is a valid int spelling even when a negative number of seats is forbidden.
Program.cs · A cast does not parse numeric text
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Compilation fails: string has no such direct cast to int.The characters 1 and 2 form a string, not an int representation waiting to be narrowed. Numeric casts and reading the spelling of a number are different operations. Use parsing for numeric text.
Program.cs · Read known numeric text
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Seats plus one: 13
Text plus one: 121
Parse interprets numeric text and returns a number. Adding 1 to that number is arithmetic; adding 1 to the original string concatenates text. This sample has known valid input. Untrusted input can be malformed or out of range, which is why the next example checks whether parsing succeeded.
Program.cs · Inspect parsing success separately from its result
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Parsed: False
Result slot: 0
TryParse returns a Boolean success flag and writes the numeric result into the out variable. On failure this overload writes zero, but that zero is not a successfully supplied seat count. Do not ignore parsed. In the next lesson, you will use a branch to choose what to do with real input.
Formatting is the boundary from values to display text
ToString converts a value into text suitable for output. A format such as F2 requests two fractional digits; a culture provider determines conventions such as the decimal separator. Choose those conventions intentionally when generating consistent machine-facing text or displaying localized output.
The example fixes the label culture explicitly with CultureInfo.InvariantCulture. It also keeps price as decimal so later arithmetic can still use the value. A formatted string belongs in the display layer; feeding it back into calculations introduces unnecessary parsing and culture assumptions.
Formatting may round what a reader sees, while casting can change the numeric value you store. Neither operation fixes a calculation that already lost information. Avoid treating a two-decimal label as proof that the underlying calculation used the right operands.
Program.cs · Turn a numeric value into display text
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Label: 14.50
Text suffix: 14.50 USD
Stored value: 14.5
ToString returns display text; it does not replace price with a string. F2 requests two fractional digits. InvariantCulture makes the label use a decimal point consistently. The final interpolation uses the environment culture; its reference here matches local preview. Formatting can round a display, but it is not a substitute for an arithmetic rounding policy.
Place the conversion before the operation that needs it
An expression runs according to its operands and grouping. In (double)(a / b), the parenthesized division happens with the original operand types first. When both are int, the fractional part is already gone before the double conversion.
In (double)a / b, the conversion changes one operand before division. That causes fractional division for these operands. The original a and b variables are still integers. Changing the expression can be enough; you do not need to change every source field to double.
This rule generalizes to other intermediate results: converting the final answer cannot repair an earlier overflow or precision loss. Trace the expression in order. First identify the operand types, then identify the operation, and finally identify when the result is converted or formatted.
Guided repair: the right cast in the wrong place
The author added a cast but still reports 3.0 for seven tasks across two sessions. Trace the parentheses, identify the types at the moment division happens, and move the conversion to the correct operand.
Program.cs · A cast after division is too late
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
Parentheses force integer division first. Seven divided by two produces 3 as an int, and only then is 3 converted to double. The syntax contains a cast, but its placement does not meet the intended calculation.
Repair checkpoints
- Predict why the original prints 3.0 before running it.
- Preserve int for completedTasks and sessionCount.
- Make the reference values produce 3.5 without hardcoding the answer.
- Test 1 task across 2 sessions: expect 0.5. Test 8 across 2: expect 4.0.
- Assume sessionCount is positive for this exercise; a production calculation needs an invalid-denominator policy.
Reveal the repair and explanation
Program.cs · Convert an operand before division
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
The cast applies to completedTasks before division. That operand is now double, so the other operand participates in fractional division too. The original integer variables keep their types and values. This exercise assumes a positive session count; handling an invalid denominator is a separate requirement.
Independent lab: repair a delivery report
Build a fixed-input delivery summary. It should retain a fractional average, report only whole distance units by truncation, and add a known text count numerically. The starter runs but uses three unsuitable conversions.
Acceptance checklist
- Keep totalMinutes = 95 and deliveryCount = 4 as int variables.
- Calculate a fractional average and display 23.75 using two decimal places.
- For measuredDistance = 12.8, report 12 whole units using a checked truncating conversion.
- Interpret the known-valid extraStopsText = "2" as an integer, then add it to deliveryCount.
- Print Total stops: 6, not 42.
- Explain why this fixed-input solution still needs validation before accepting arbitrary user text.
Program.cs · Delivery report starter
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Average minutes: 23.00
Whole distance: 13
Total stops: 42
Three independent mismatches are hidden in otherwise runnable code. The average casts too late, the distance uses rounding when the requirement is truncation, and the stop calculation joins text instead of adding numbers. Fix each boundary for a reason you can explain.
Show hints
- Compare (double)(a / b) with (double)a / b.
- Convert.ToInt32(double) rounds; an explicit int cast truncates for an in-range value.
- Adding a string performs text concatenation. Parse known numeric text before adding.
Reveal the reference solution
Program.cs · Delivery report reference solution
Predict the result, edit Program.cs, then run your program.
Standard input · optional
Select Run code to compile and execute this program.
Average minutes: 23.75
Whole distance: 12
Total stops: 6
The operand conversion preserves the fractional average, the checked cast truncates the in-range distance, and parsing makes the final addition numeric. These are fixed known inputs. Parse is not validation for arbitrary text, and a checked conversion enforces representation bounds rather than delivery business rules.
Changed-input checks
- With totalMinutes = 1 and deliveryCount = 2, expect average 0.50 and total stops 4.
- With distance = -12.8, the cast yields -12. Explain why accepting negative distance is a separate domain decision.
- With extraStopsText = "0", expect total stops 4; zero is valid numeric text.
- With extraStopsText = "two", Parse throws FormatException. Explain how TryParse would let a later input-validation branch handle failure.
- A distance beyond int range must fail the checked conversion rather than produce a misleading whole-unit count.
Recap: choose the operation for the boundary
Identify the source, destination, range and intended policy. These operations serve different purposes; they are not interchangeable fixes for a compiler message.
int → long- Ordinary assignment preserves every int value; the source remains int.
(int)value- An explicit numeric conversion. For an in-range fraction it truncates toward zero.
checked((byte)value)- Detect a value that will not fit instead of silently losing integer bits.
Convert.ToInt32(double)- Round to nearest, with exact midpoints choosing the even integer.
int.TryParse(text, out result)- Read numeric text and keep the success flag; zero alone does not prove success.
value.ToString(format, culture)- Create display text without replacing the numeric source value.
(double)a / b- Convert before division to preserve a fractional result for these operands.
Frequently asked questions
Does a cast change the original variable?
The numeric casts in this lesson produce converted values. The source variable keeps its declared type and stored value unless you explicitly assign something back to it.
Does an int cast round 12.8 to 13?
No. For an in-range fractional value, it truncates toward zero, so 12.8 becomes 12 and -12.8 becomes -12. Choose a rounding method when rounding is the requirement.
Why does byte x = 12 work without a cast?
C# has a constant-expression rule for certain integer conversions when the constant fits. That does not mean any int variable can be assigned to byte implicitly.
Is an implicit conversion always exact?
No. For example, long-to-double can lose integer precision at large magnitudes. int-to-long preserves every int value, but that guarantee should not be generalized to all numeric conversions.
Why not cast a string containing a number?
A numeric cast changes numeric representation; parsing interprets text. Use Parse for known valid numeric text or TryParse when ordinary parsing failures must be checked.
Does checked stop every information loss?
No. It detects range failures for the numeric conversions shown here. It does not preserve a discarded fraction, restore floating-point precision or enforce business rules.
Can you explain the conversion?
Reason about cast placement, range, precision and the difference between text and numbers. Read the feedback for each answer.
Next step and references
A conversion creates a result in another representation. Trace when it happens, choose truncation or rounding deliberately, and distinguish casting from parsing and formatting. Check range and precision separately; a successful conversion does not prove valid application data.
Next is C# User Input. You will read text from the console, check whether it represents an acceptable number, and decide what to do when input is missing or invalid.
Official references
Original SovranCode explanations, examples and exercises. Technical details were checked against these references.
- Microsoft Learn: Built-in numeric conversions
- Microsoft Learn: Casting and type conversions
- Microsoft Learn: checked and unchecked
- Microsoft Learn: Parse and TryParse
- Microsoft Learn: Convert.ToInt32 overloads
Last updated: .