Code for mapping voltage on a sensor and outputting a value in liters
Curve fitting
Well, a lazy way to do it is just to fit a curve to it, using Excel, or Libreoffice, Google Docs or whatever.  Here's gnuplot being used that way to fit a cubic polynomial to it:
&quot;points.txt&quot; containing your data, tab-delimited:
4.04    94
3.981   88
3.902   84
3.81    81
3.703   77
3.637   72
3.564   69
3.483   65
3.393   61
3.293   57
3.18    53
3.053   50
2.908   46
2.546   42
2.192   38
1.726   35

Gnuplot session:
gnuplot&gt; f(x) = A*(x**3) + B*(x**2) + C*x + D
gnuplot&gt; fit f(x) &quot;points.txt&quot; via A, B, C, D
iter      chisq       delta/lim  lambda   A             B             C             D            
   0 1.8323200047e+01   0.00e+00  9.28e+01    2.818423e+00  -1.100151e+01   1.447650e+01   2.873579e+01
   1 1.8323200047e+01  -4.65e-10  9.28e+00    2.818423e+00  -1.100151e+01   1.447650e+01   2.873579e+01
iter      chisq       delta/lim  lambda   A             B             C             D            

After 1 iterations the fit converged.
final sum of squares of residuals : 18.3232
rel. change during last iteration : -4.6534e-15

degrees of freedom    (FIT_NDF)                        : 12
rms of residuals      (FIT_STDFIT) = sqrt(WSSR/ndf)    : 1.23569
variance of residuals (reduced chisquare) = WSSR/ndf   : 1.52693

Final set of parameters            Asymptotic Standard Error
=======================            ==========================
A               = 2.81842          +/- 1.156        (41.02%)
B               = -11.0015         +/- 10.22        (92.86%)
C               = 14.4765          +/- 29.11        (201.1%)
D               = 28.7358          +/- 26.52        (92.27%)

correlation matrix of the fit parameters:
                A      B      C      D      
A               1.000 
B              -0.998  1.000 
C               0.990 -0.997  1.000 
D              -0.974  0.987 -0.996  1.000 

And here's what looks like against the actual data with plot &quot;points.txt&quot; with linespoints, f(x):

So you have a function:
float f(float x) {
  static const float A =   2.81842;
  static const float B = -11.0015 ;    
  static const float C =  14.4765 ;
  static const float D =  28.7358 ;
  const float x_squared = x * x;
  const float x_cubed   = x * x_squared;

  return A * x_cubed + B * x_squared + C * x + D;
}

If you don't care about the code size and the slowness of float arithmetic, then that might be okay.
Otherwise you may want to convert that function to something like fixed point arithmetic.
A cubic polynomial may be overkill, but then whole technique may be also.
Peicewise Linear
You can make a table of you points and just do what graphing programs typically do, linear interpolation between the points.
float f(float x) {
  static const struct pt {
    float v;
    int l;
  } points[] = {
    {0.0  ,  0},  // I put this here for testing, but put a better value here.
    {1.726, 35},
    {2.192, 38},
    {2.546, 42},
    {2.908, 46},
    {3.053, 50},
    {3.18 , 53},
    {3.293, 57},
    {3.393, 61},
    {3.483, 65},
    {3.564, 69},
    {3.637, 72},
    {3.703, 77},
    {3.81 , 81},
    {3.902, 84},
    {3.981, 88},
    {4.04 , 94}
    // you should probably have another entry here so that readings up to 5V work.
  };
  static const size_t table_len = sizeof points / sizeof points[0];

  for (size_t i = 0; i &lt; table_len - 1; ++i) {
    if (x &gt;= points[i].v &amp;&amp; x &lt; points[i + 1].v) {
      return 
            (x - points[i].v) 
          / (points[i + 1].v - points[i].v) 
          * (points[i + 1].l - points[i].l)
          + points[i].l;
    }
  }

  return -1; 
}

Sitting in the center of this in the return statement is basically a crude implementation of Arduino's map(), but for floating point, which again, you don't really need floating point, even if you want to use this piecewise approach.
In short, you find which two entries of the table you're sitting between, and apply what amounts to map() on the line segment formed by the two table entries.
PROGMEM
Either of the curve fitting or piecewise approach could be used to generate a much larger lookup table out of which you could just chose the nearest point.
If you're doing this on an AVR-based Arduino, the table for the piecewise linear approach or a pure lookup table approach could use PROGMEM for the array.
Regarding float and fixed point
Any of these techniques could be adapted to fixed-point math.
Which ever you use, if you're on AVR based Arduino, keep in mind that there's no hardware floating point support at all, and although double exists as a type it is implemented in avr-gcc as a single precision float.
If you're using one of the non-AVR based Arduinos, it may have support for float (and maybe for double), in which case the generated code may not be so bad, though may still be slower than using integers.
If you can do everything with integers, resulting code should be a lot smaller and faster.  I'm guessing for what you're doing, if either of them are more important, it's probably being smaller.
For using integers, you may be able to keep your input &quot;voltage&quot; as an ADC count, not having an intermediate voltage calculation.  And even if you want single digit liters when you're done, the intermediate interpolation, curve fitting, or table lookup may require that you work in something like centiliters, milliliters, so that values like the A constant from the curve-fitting example can be represented as an integer, e.g.  2818 mL  rather than a cruder 3 L.  If you're not using a plain lookup table and finding the nearest entry, that is if you're using the curve or piecewise linear approach, doing everything in integer arithmetic from ADC count to mL.  Although you'll need to run another curve fitting, since the A, B, etc will representing mL-per-ADC-count not L-per-Volt.  Or scale them from the value given.
And there are other options, piecewise cubic splines, etc.