Cast string to number

WP doesn’t “look” at the type of data you have, it just has a default of CHAR. So if you dump your current query you will see respective type cast: CAST(wp_postmeta.meta_value AS CHAR).

To make it work you need to specify type argument explicitly 'type' => 'NUMERIC' and CAST will change accordingly: CAST(wp_postmeta.meta_value AS SIGNED).

See Custom Field Parameters in Codex for more on supported types.

For the record here is how you can dump generated SQL, without involving too many moving parts:

$meta_query_args = array(
    'relation' => 'OR',
    array(
        'key'     => 'price',
        'value'   => array( 80, 100 ),
        'compare' => 'BETWEEN',
        'type'    => 'NUMERIC',
    ),
);

$meta_query = new WP_Meta_Query( $meta_query_args );
$sql        = $meta_query->get_sql( 'post', $wpdb->posts, 'ID' );

/*
array (2) [
    'join' => string (64) " INNER JOIN wp_postmeta ON ( wp_posts.ID = wp_postmeta.post_id )"
    'where' => string (112) " AND (
  ( wp_postmeta.meta_key = 'price' AND CAST(wp_postmeta.meta_value AS SIGNED) BETWEEN '80' AND '100' )
)"
]
*/